From 05092e55f166f6c54526c2539a8ed534642bb13d Mon Sep 17 00:00:00 2001 From: kscheidecker Date: Wed, 19 Feb 2025 15:53:41 +0100 Subject: [PATCH] better structure --- .../{utils => landmarks}/cluster_manager.py | 4 +- .../{utils => landmarks}/landmarks_manager.py | 4 +- backend/src/main.py | 41 ++++--------------- backend/src/overpass/overpass.py | 12 +++++- backend/src/structs/landmark.py | 24 ----------- backend/src/structs/toilets.py | 26 ++++++++++++ backend/src/toilets/toilet_routes.py | 37 +++++++++++++++++ .../src/{utils => toilets}/toilets_manager.py | 4 +- backend/src/utils/{utils.py => bbox.py} | 0 9 files changed, 88 insertions(+), 64 deletions(-) rename backend/src/{utils => landmarks}/cluster_manager.py (99%) rename backend/src/{utils => landmarks}/landmarks_manager.py (97%) create mode 100644 backend/src/structs/toilets.py create mode 100644 backend/src/toilets/toilet_routes.py rename backend/src/{utils => toilets}/toilets_manager.py (98%) rename backend/src/utils/{utils.py => bbox.py} (100%) diff --git a/backend/src/utils/cluster_manager.py b/backend/src/landmarks/cluster_manager.py similarity index 99% rename from backend/src/utils/cluster_manager.py rename to backend/src/landmarks/cluster_manager.py index 86f926a..40f1fd0 100644 --- a/backend/src/utils/cluster_manager.py +++ b/backend/src/landmarks/cluster_manager.py @@ -8,8 +8,8 @@ from pydantic import BaseModel from ..overpass.overpass import Overpass, get_base_info from ..structs.landmark import Landmark -from .get_time_distance import get_distance -from .utils import create_bbox +from ..utils.get_time_distance import get_distance +from ..utils.bbox import create_bbox diff --git a/backend/src/utils/landmarks_manager.py b/backend/src/landmarks/landmarks_manager.py similarity index 97% rename from backend/src/utils/landmarks_manager.py rename to backend/src/landmarks/landmarks_manager.py index b015919..0980f43 100644 --- a/backend/src/utils/landmarks_manager.py +++ b/backend/src/landmarks/landmarks_manager.py @@ -4,10 +4,10 @@ import yaml from ..structs.preferences import Preferences from ..structs.landmark import Landmark -from .take_most_important import take_most_important +from ..utils.take_most_important import take_most_important from .cluster_manager import ClusterManager from ..overpass.overpass import Overpass, get_base_info -from .utils import create_bbox +from ..utils.bbox import create_bbox from ..constants import AMENITY_SELECTORS_PATH, LANDMARK_PARAMETERS_PATH, OPTIMIZER_PARAMETERS_PATH diff --git a/backend/src/main.py b/backend/src/main.py index 112e52e..7580c1c 100644 --- a/backend/src/main.py +++ b/backend/src/main.py @@ -7,12 +7,12 @@ from fastapi import FastAPI, HTTPException, BackgroundTasks, Query from fastapi.encoders import jsonable_encoder from .logging_config import configure_logging -from .structs.landmark import Landmark, Toilets +from .structs.landmark import Landmark from .structs.preferences import Preferences from .structs.linked_landmarks import LinkedLandmarks from .structs.trip import Trip -from .utils.landmarks_manager import LandmarkManager -from .utils.toilets_manager import ToiletsManager +from .landmarks.landmarks_manager import LandmarkManager +from .toilets.toilet_routes import router as toilets_router from .optimization.optimizer import Optimizer from .optimization.refiner import Refiner from .overpass.overpass import fill_cache @@ -38,6 +38,10 @@ async def lifespan(app: FastAPI): app = FastAPI(lifespan=lifespan) + +app.include_router(toilets_router) + + @app.post("/trip/new") def new_trip(preferences: Preferences, start: tuple[float, float], @@ -67,6 +71,8 @@ def new_trip(preferences: Preferences, end = start logger.info("No end coordinates provided. Using start=end.") + logger.info(f"Requested new trip generation. Details:\n\tCoordinates: {start}\n\tTime: {preferences.max_time_minute}\n\tSightseeing: {preferences.sightseeing.score}\n\tNature: {preferences.nature.score}\n\tShopping: {preferences.shopping.score}") + start_landmark = Landmark(name='start', type='start', location=(start[0], start[1]), @@ -225,32 +231,3 @@ def update_trip_time(trip_uuid: str, removed_landmark_uuid: str) -> Trip: trip = Trip.from_linked_landmarks(linked_tour, cache_client) return trip - - - -@app.post("/toilets/new") -def get_toilets(location: tuple[float, float] = Query(...), radius: int = 500) -> list[Toilets] : - """ - Endpoint to find toilets within a specified radius from a given location. - - This endpoint expects the `location` and `radius` as **query parameters**, not in the request body. - - Args: - location (tuple[float, float]): The latitude and longitude of the location to search from. - radius (int, optional): The radius (in meters) within which to search for toilets. Defaults to 500 meters. - - Returns: - list[Toilets]: A list of Toilets objects that meet the criteria. - """ - if location is None: - raise HTTPException(status_code=406, detail="Coordinates not provided or invalid") - if not (-90 <= location[0] <= 90 or -180 <= location[1] <= 180): - raise HTTPException(status_code=422, detail="Start coordinates not in range") - - toilets_manager = ToiletsManager(location, radius) - - try : - toilets_list = toilets_manager.generate_toilet_list() - return toilets_list - except KeyError as exc: - raise HTTPException(status_code=404, detail="No toilets found") from exc diff --git a/backend/src/overpass/overpass.py b/backend/src/overpass/overpass.py index fabc0ee..2cd0058 100644 --- a/backend/src/overpass/overpass.py +++ b/backend/src/overpass/overpass.py @@ -1,5 +1,6 @@ """Module allowing connexion to overpass api and fectch data from OSM.""" import os +import time import urllib import math import logging @@ -153,7 +154,7 @@ class Overpass : - If no conditions are provided, the query will just use the `selector` to filter the OSM elements without additional constraints. """ - query = '[out:json];(' + query = '[out:json][timeout:20];(' # convert the bbox to string. bbox_str = f"({','.join(map(str, bbox))})" @@ -399,18 +400,25 @@ def fill_cache(): """ overpass = Overpass() + n_files = 0 + total = 0 + with os.scandir(OSM_CACHE_DIR) as it: for entry in it: if entry.is_file() and entry.name.startswith('hollow_'): - + total += 1 try : # Read the whole file content as a string with open(entry.path, 'r', encoding='utf-8') as f: # load data and fill the cache with the query and key json_data = json.load(f) overpass.fill_cache(json_data) + n_files += 1 + time.sleep(1) # Now delete the file as the cache is filled os.remove(entry.path) except Exception as exc : overpass.logger.error(f'An error occured while parsing file {entry.path} as .json file: {str(exc)}') + + overpass.logger.info(f"Successfully filled {n_files}/{total} cache files.") \ No newline at end of file diff --git a/backend/src/structs/landmark.py b/backend/src/structs/landmark.py index c62bbde..d6250a8 100644 --- a/backend/src/structs/landmark.py +++ b/backend/src/structs/landmark.py @@ -1,5 +1,4 @@ """Definition of the Landmark class to handle visitable objects across the world.""" - from typing import Optional, Literal, List from uuid import uuid4, UUID from pydantic import BaseModel, ConfigDict, Field @@ -129,26 +128,3 @@ class Landmark(BaseModel) : return (self.uuid == value.uuid or self.osm_id == value.osm_id or (self.name == value.name and self.distance(value) < 0.001)) - - -class Toilets(BaseModel) : - """ - Model for toilets. When false/empty the information is either false either not known. - """ - location : tuple - wheelchair : Optional[bool] = False - changing_table : Optional[bool] = False - fee : Optional[bool] = False - opening_hours : Optional[str] = "" - - - def __str__(self) -> str: - """ - String representation of the Toilets object. - - Returns: - str: A formatted string with the toilets location. - """ - return f'Toilets @{self.location}' - - model_config = ConfigDict(from_attributes=True) diff --git a/backend/src/structs/toilets.py b/backend/src/structs/toilets.py new file mode 100644 index 0000000..5a06943 --- /dev/null +++ b/backend/src/structs/toilets.py @@ -0,0 +1,26 @@ +"""Definition of the Toilets class.""" +from typing import Optional +from pydantic import BaseModel, ConfigDict + + +class Toilets(BaseModel) : + """ + Model for toilets. When false/empty the information is either false either not known. + """ + location : tuple + wheelchair : Optional[bool] = False + changing_table : Optional[bool] = False + fee : Optional[bool] = False + opening_hours : Optional[str] = "" + + + def __str__(self) -> str: + """ + String representation of the Toilets object. + + Returns: + str: A formatted string with the toilets location. + """ + return f'Toilets @{self.location}' + + model_config = ConfigDict(from_attributes=True) diff --git a/backend/src/toilets/toilet_routes.py b/backend/src/toilets/toilet_routes.py new file mode 100644 index 0000000..20d0383 --- /dev/null +++ b/backend/src/toilets/toilet_routes.py @@ -0,0 +1,37 @@ +from fastapi import HTTPException, APIRouter, Query + +from ..structs.toilets import Toilets +from .toilets_manager import ToiletsManager + + +# Define the API router +router = APIRouter() + + +@router.post("/toilets/new") +def get_toilets(location: tuple[float, float] = Query(...), radius: int = 500) -> list[Toilets] : + """ + Endpoint to find toilets within a specified radius from a given location. + + This endpoint expects the `location` and `radius` as **query parameters**, not in the request body. + + Args: + location (tuple[float, float]): The latitude and longitude of the location to search from. + radius (int, optional): The radius (in meters) within which to search for toilets. Defaults to 500 meters. + + Returns: + list[Toilets]: A list of Toilets objects that meet the criteria. + """ + if location is None: + raise HTTPException(status_code=406, detail="Coordinates not provided or invalid") + if not (-90 <= location[0] <= 90 or -180 <= location[1] <= 180): + raise HTTPException(status_code=422, detail="Start coordinates not in range") + + toilets_manager = ToiletsManager(location, radius) + + try : + toilets_list = toilets_manager.generate_toilet_list() + except KeyError as exc: + raise HTTPException(status_code=404, detail="No toilets found") from exc + + return toilets_list diff --git a/backend/src/utils/toilets_manager.py b/backend/src/toilets/toilets_manager.py similarity index 98% rename from backend/src/utils/toilets_manager.py rename to backend/src/toilets/toilets_manager.py index 57c0d83..9abf7fa 100644 --- a/backend/src/utils/toilets_manager.py +++ b/backend/src/toilets/toilets_manager.py @@ -2,8 +2,8 @@ import logging from ..overpass.overpass import Overpass, get_base_info -from ..structs.landmark import Toilets -from .utils import create_bbox +from ..structs.toilets import Toilets +from ..utils.bbox import create_bbox # silence the overpass logger diff --git a/backend/src/utils/utils.py b/backend/src/utils/bbox.py similarity index 100% rename from backend/src/utils/utils.py rename to backend/src/utils/bbox.py