shorter lines
Some checks failed
Build and deploy the backend to staging / Build and push image (pull_request) Successful in 2m27s
Run linting on the backend code / Build (pull_request) Failing after 36s
Run testing on the backend code / Build (pull_request) Failing after 1m25s
Build and deploy the backend to staging / Deploy to staging (pull_request) Successful in 15s
Some checks failed
Build and deploy the backend to staging / Build and push image (pull_request) Successful in 2m27s
Run linting on the backend code / Build (pull_request) Failing after 36s
Run testing on the backend code / Build (pull_request) Failing after 1m25s
Build and deploy the backend to staging / Deploy to staging (pull_request) Successful in 15s
This commit is contained in:
parent
eec3be5122
commit
70a93c7143
@ -345,7 +345,7 @@ indent-after-paren=4
|
||||
indent-string=' '
|
||||
|
||||
# Maximum number of characters on a single line.
|
||||
max-line-length=100
|
||||
max-line-length=105
|
||||
|
||||
# Maximum number of lines in a module.
|
||||
max-module-lines=1000
|
||||
@ -410,7 +410,6 @@ logging-modules=logging
|
||||
|
||||
|
||||
[MESSAGES CONTROL]
|
||||
disable=E0401
|
||||
|
||||
# Only show warnings with the listed confidence levels. Leave empty to show
|
||||
# all. Valid levels: HIGH, CONTROL_FLOW, INFERENCE, INFERENCE_FAILURE,
|
||||
@ -439,7 +438,8 @@ disable=raw-checker-failed,
|
||||
deprecated-pragma,
|
||||
use-symbolic-message-instead,
|
||||
use-implicit-booleaness-not-comparison-to-string,
|
||||
use-implicit-booleaness-not-comparison-to-zero
|
||||
use-implicit-booleaness-not-comparison-to-zero,
|
||||
import-error
|
||||
|
||||
# Enable the message, report, category or checker with the given id(s). You can
|
||||
# either give multiple identifier separated by comma (,) or put this option
|
||||
|
@ -22,31 +22,54 @@ refiner = Refiner(optimizer=optimizer)
|
||||
|
||||
|
||||
@app.post("/trip/new")
|
||||
def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[float, float] | None = None) -> Trip:
|
||||
def new_trip(preferences: Preferences,
|
||||
start: tuple[float, float],
|
||||
end: tuple[float, float] | None = None) -> Trip:
|
||||
"""
|
||||
Main function to call the optimizer.
|
||||
|
||||
Args:
|
||||
preferences (Preferences) : the preferences specified by the user as the post body
|
||||
start (tuple) : the coordinates of the starting point as a tuple of floats (as url query parameters)
|
||||
end (tuple) : the coordinates of the finishing point as a tuple of floats (as url query parameters)
|
||||
preferences : the preferences specified by the user as the post body
|
||||
start : the coordinates of the starting point
|
||||
end : the coordinates of the finishing point
|
||||
Returns:
|
||||
(uuid) : The uuid of the first landmark in the optimized route
|
||||
"""
|
||||
if preferences is None:
|
||||
raise HTTPException(status_code=406, detail="Preferences not provided")
|
||||
if preferences.shopping.score == 0 and preferences.sightseeing.score == 0 and preferences.nature.score == 0:
|
||||
raise HTTPException(status_code=406, detail="All preferences are 0.")
|
||||
raise HTTPException(status_code=406,
|
||||
detail="Preferences not provided or incomplete.")
|
||||
if (preferences.shopping.score == 0 and
|
||||
preferences.sightseeing.score == 0 and
|
||||
preferences.nature.score == 0) :
|
||||
raise HTTPException(status_code=406,
|
||||
detail="All preferences are 0.")
|
||||
if start is None:
|
||||
raise HTTPException(status_code=406, detail="Start coordinates not provided")
|
||||
raise HTTPException(status_code=406,
|
||||
detail="Start coordinates not provided")
|
||||
if not (-90 <= start[0] <= 90 or -180 <= start[1] <= 180):
|
||||
raise HTTPException(status_code=423, detail="Start coordinates not in range")
|
||||
raise HTTPException(status_code=423,
|
||||
detail="Start coordinates not in range")
|
||||
if end is None:
|
||||
end = start
|
||||
logger.info("No end coordinates provided. Using start=end.")
|
||||
|
||||
start_landmark = Landmark(name='start', type='start', location=(start[0], start[1]), osm_type='start', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
|
||||
end_landmark = Landmark(name='finish', type='finish', location=(end[0], end[1]), osm_type='end', osm_id=0, attractiveness=0, must_do=True, n_tags = 0)
|
||||
start_landmark = Landmark(name='start',
|
||||
type='start',
|
||||
location=(start[0], start[1]),
|
||||
osm_type='start',
|
||||
osm_id=0,
|
||||
attractiveness=0,
|
||||
must_do=True,
|
||||
n_tags = 0)
|
||||
|
||||
end_landmark = Landmark(name='finish',
|
||||
type='finish',
|
||||
location=(end[0], end[1]),
|
||||
osm_type='end',
|
||||
osm_id=0,
|
||||
attractiveness=0,
|
||||
must_do=True,
|
||||
n_tags = 0)
|
||||
|
||||
# Generate the landmarks from the start location
|
||||
landmarks, landmarks_short = manager.generate_landmarks_list(
|
||||
@ -67,10 +90,12 @@ def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[fl
|
||||
raise HTTPException(status_code=500, detail="Optimzation took too long") from exc
|
||||
|
||||
# Second stage optimization
|
||||
refined_tour = refiner.refine_optimization(landmarks, base_tour, preferences.max_time_minute, preferences.detour_tolerance_minute)
|
||||
refined_tour = refiner.refine_optimization(landmarks, base_tour,
|
||||
preferences.max_time_minute,
|
||||
preferences.detour_tolerance_minute)
|
||||
|
||||
linked_tour = LinkedLandmarks(refined_tour)
|
||||
# upon creation of the trip, persistence of both the trip and its landmarks is ensured
|
||||
# upon creation of the trip, persistence of both the trip and its landmarks is ensured.
|
||||
trip = Trip.from_linked_landmarks(linked_tour, cache_client)
|
||||
return trip
|
||||
|
||||
@ -109,4 +134,5 @@ def get_landmark(landmark_uuid: str) -> Landmark:
|
||||
landmark = cache_client.get(f"landmark_{landmark_uuid}")
|
||||
return landmark
|
||||
except KeyError as exc:
|
||||
raise HTTPException(status_code=404, detail="Landmark not found") from exc
|
||||
raise HTTPException(status_code=404,
|
||||
detail="Landmark not found") from exc
|
||||
|
@ -70,13 +70,14 @@ class Landmark(BaseModel) :
|
||||
str: A formatted string with the landmark's type, name, location, attractiveness score,
|
||||
time to the next landmark (if available), and whether the landmark is secondary.
|
||||
"""
|
||||
time_to_next_str = f", time_to_next={self.time_to_reach_next}" if self.time_to_reach_next else ""
|
||||
t_to_next_str = f", time_to_next={self.time_to_reach_next}" if self.time_to_reach_next else ""
|
||||
is_secondary_str = ", secondary" if self.is_secondary else ""
|
||||
type_str = '(' + self.type + ')'
|
||||
if self.type in ["start", "finish", "nature", "shopping"] :
|
||||
type_str += '\t '
|
||||
|
||||
return f'Landmark{type_str}: [{self.name} @{self.location}, score={self.attractiveness}{time_to_next_str}{is_secondary_str}]'
|
||||
return (f'Landmark{type_str}: [{self.name} @{self.location}, '
|
||||
f'score={self.attractiveness}{t_to_next_str}{is_secondary_str}]')
|
||||
|
||||
def distance(self, value: 'Landmark') -> float:
|
||||
"""
|
||||
@ -113,4 +114,6 @@ class Landmark(BaseModel) :
|
||||
# in particular, if two objects are equal, their hash must be equal
|
||||
# uuid and osm_id are just shortcuts to avoid comparing all the properties
|
||||
# if they are equal, we know that the name is also equal and in turn the hash is equal
|
||||
return self.uuid == value.uuid or self.osm_id == value.osm_id or (self.name == value.name and self.distance(value) < 0.001)
|
||||
return (self.uuid == value.uuid or
|
||||
self.osm_id == value.osm_id or
|
||||
(self.name == value.name and self.distance(value) < 0.001))
|
||||
|
@ -6,9 +6,11 @@ from ..utils.get_time_separation import get_time
|
||||
class LinkedLandmarks:
|
||||
"""
|
||||
A list of landmarks that are linked together, e.g. in a route.
|
||||
Each landmark serves as a node in the linked list, but since we expect these to be consumed through the rest API,
|
||||
a pythonic reference to the next landmark is not well suited. Instead we use the uuid of the next landmark
|
||||
to reference the next landmark in the list. This is not very efficient, but appropriate for the expected use case
|
||||
Each landmark serves as a node in the linked list, but since we expect
|
||||
these to be consumed through the rest API, a pythonic reference to the next
|
||||
landmark is not well suited. Instead we use the uuid of the next landmark
|
||||
to reference the next landmark in the list. This is not very efficient,
|
||||
but appropriate for the expected use case
|
||||
("short" trips with onyl few landmarks).
|
||||
"""
|
||||
|
||||
@ -21,7 +23,8 @@ class LinkedLandmarks:
|
||||
where the first landmark is the starting point and the last landmark is the end point.
|
||||
|
||||
Args:
|
||||
data (list[Landmark], optional): The list of landmarks that are linked together. Defaults to None.
|
||||
data (list[Landmark], optional): The list of landmarks that are linked together.
|
||||
Defaults to None.
|
||||
"""
|
||||
self._landmarks = data if data else []
|
||||
self._link_landmarks()
|
||||
@ -29,7 +32,8 @@ class LinkedLandmarks:
|
||||
|
||||
def _link_landmarks(self) -> None:
|
||||
"""
|
||||
Create the links between the landmarks in the list by setting their .next_uuid and the .time_to_next attributes.
|
||||
Create the links between the landmarks in the list by setting their
|
||||
.next_uuid and the .time_to_next attributes.
|
||||
"""
|
||||
|
||||
# Mark secondary landmarks as such
|
||||
@ -57,12 +61,12 @@ class LinkedLandmarks:
|
||||
if len(scores) >= 10:
|
||||
threshold_score = scores[9]
|
||||
else:
|
||||
# If there are fewer than 10 landmarks, use the lowest score in the list as the threshold
|
||||
# If there are fewer than 10 landmarks, use the lowest score as the threshold
|
||||
threshold_score = min(scores) if scores else 0
|
||||
|
||||
# Update 'is_secondary' for landmarks with attractiveness below the threshold score
|
||||
for landmark in self._landmarks:
|
||||
if landmark.attractiveness < threshold_score and landmark.type not in ["start", "finish"]:
|
||||
if (landmark.attractiveness < threshold_score and landmark.type not in ["start", "finish"]):
|
||||
landmark.is_secondary = True
|
||||
|
||||
|
||||
|
@ -36,8 +36,11 @@ class Trip(BaseModel):
|
||||
|
||||
# Store the trip in the cache
|
||||
cache_client.set(f"trip_{trip.uuid}", trip)
|
||||
# make sure to await the result (noreply=False). Otherwise the cache might not be inplace when the trip is actually requested
|
||||
cache_client.set_many({f"landmark_{landmark.uuid}": landmark for landmark in landmarks}, expire=3600, noreply=False)
|
||||
|
||||
# Make sure to await the result (noreply=False).
|
||||
# Otherwise the cache might not be inplace when the trip is actually requested.
|
||||
cache_client.set_many({f"landmark_{landmark.uuid}": landmark for landmark in landmarks},
|
||||
expire=3600, noreply=False)
|
||||
# is equivalent to:
|
||||
# for landmark in landmarks:
|
||||
# cache_client.set(f"landmark_{landmark.uuid}", landmark, expire=3600)
|
||||
|
@ -57,7 +57,11 @@ def test_bellecour(client, request) : # pylint: disable=redefined-outer-name
|
||||
response = client.post(
|
||||
"/trip/new",
|
||||
json={
|
||||
"preferences": {"sightseeing": {"type": "sightseeing", "score": 5}, "nature": {"type": "nature", "score": 5}, "shopping": {"type": "shopping", "score": 5}, "max_time_minute": duration_minutes, "detour_tolerance_minute": 0},
|
||||
"preferences": {"sightseeing": {"type": "sightseeing", "score": 5},
|
||||
"nature": {"type": "nature", "score": 5},
|
||||
"shopping": {"type": "shopping", "score": 5},
|
||||
"max_time_minute": duration_minutes,
|
||||
"detour_tolerance_minute": 0},
|
||||
"start": [45.7576485, 4.8330241]
|
||||
}
|
||||
)
|
||||
@ -79,7 +83,11 @@ def test_bellecour(client, request) : # pylint: disable=redefined-outer-name
|
||||
# response = client.post(
|
||||
# "/trip/new",
|
||||
# json={
|
||||
# "preferences": {"sightseeing": {"type": "sightseeing", "score": 1}, "nature": {"type": "nature", "score": 1}, "shopping": {"type": "shopping", "score": 1}, "max_time_minute": 360, "detour_tolerance_minute": 0},
|
||||
# "preferences": {"sightseeing": {"type": "sightseeing", "score": 1},
|
||||
# "nature": {"type": "nature", "score": 1},
|
||||
# "shopping": {"type": "shopping", "score": 1},
|
||||
# "max_time_minute": 360,
|
||||
# "detour_tolerance_minute": 0},
|
||||
# "start": [48.8566, 2.3522]
|
||||
# }
|
||||
# )
|
||||
|
@ -34,7 +34,8 @@ def fetch_landmark(client, landmark_uuid: str):
|
||||
response = client.get(f"/landmark/{landmark_uuid}")
|
||||
|
||||
if response.status_code != 200:
|
||||
raise HTTPException(status_code=999, detail=f"Failed to fetch landmark with UUID {landmark_uuid}: {response.status_code}")
|
||||
raise HTTPException(status_code=999,
|
||||
detail=f"Failed to fetch landmark with UUID {landmark_uuid}: {response.status_code}")
|
||||
|
||||
json_data = response.json()
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user