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

This commit is contained in:
Helldragon67 2024-11-30 18:27:52 +01:00
parent eec3be5122
commit 70a93c7143
7 changed files with 77 additions and 32 deletions

View File

@ -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

View File

@ -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

View File

@ -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))

View File

@ -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

View File

@ -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)

View File

@ -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]
# }
# )

View File

@ -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()