cleanup path handling for easier dockerization
This commit is contained in:
		| @@ -2,6 +2,8 @@ on: | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - main | ||||
|     paths: | ||||
|       - backend/** | ||||
| 
 | ||||
| name: Build and push docker image | ||||
| 
 | ||||
| @@ -6,6 +6,11 @@ COPY Pipfile Pipfile.lock . | ||||
| RUN pip install pipenv | ||||
| RUN pipenv install --deploy --system | ||||
|  | ||||
| COPY . /src | ||||
| COPY src src | ||||
|  | ||||
| CMD ["pipenv", "run", "python", "/app/src/main.py"] | ||||
| EXPOSE 8000 | ||||
|  | ||||
| # Set environment variables used by the deployment. These can be overridden by the user using this image. | ||||
| ENV NUM_WORKERS=1 | ||||
|  | ||||
| CMD ["pipenv", "run", "fastapi", "run", "src/main.py", '--port 8000', '--workers $NUM_WORKERS'] | ||||
|   | ||||
							
								
								
									
										9
									
								
								backend/src/constants.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								backend/src/constants.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| from pathlib import Path | ||||
|  | ||||
|  | ||||
| PARAMETERS_DIR = Path('src/parameters') | ||||
| AMENITY_SELECTORS_PATH = PARAMETERS_DIR / 'amenity_selectors.yaml' | ||||
| LANDMARK_PARAMETERS_PATH = PARAMETERS_DIR / 'landmark_parameters.yaml' | ||||
| OPTIMIZER_PARAMETERS_PATH = PARAMETERS_DIR / 'optimizer_parameters.yaml' | ||||
|  | ||||
| OSM_CACHE_DIR = Path('cache') | ||||
| @@ -1,9 +1,11 @@ | ||||
| import math as m | ||||
| import json, os | ||||
| import yaml | ||||
|  | ||||
| from typing import List, Tuple, Optional | ||||
| from OSMPythonTools.overpass import Overpass, overpassQueryBuilder | ||||
|  | ||||
| import constants | ||||
| from structs.landmarks import Landmark, LandmarkType | ||||
| from structs.preferences import Preferences, Preference | ||||
|  | ||||
| @@ -13,34 +15,40 @@ NATURE = LandmarkType(landmark_type='nature') | ||||
| SHOPPING = LandmarkType(landmark_type='shopping') | ||||
|  | ||||
|  | ||||
|  | ||||
| # Include the json here | ||||
| # Create a list of all things to visit given some preferences and a city. Ready for the optimizer | ||||
| def generate_landmarks(preferences: Preferences, coordinates: Tuple[float, float]) : | ||||
|     with constants.AMENITY_SELECTORS_PATH.open('r') as f: | ||||
|         amenity_selectors = yaml.safe_load(f) | ||||
|      | ||||
|     with constants.LANDMARK_PARAMETERS_PATH.open('r') as f: | ||||
|         # even though we don't use the parameters here, we already load them to avoid unnecessary io operations | ||||
|         parameters = yaml.safe_load(f) | ||||
|  | ||||
|     l_sights, l_nature, l_shop = get_amenities() | ||||
|     L = [] | ||||
|  | ||||
|     # List for sightseeing | ||||
|     if preferences.sightseeing.score != 0 : | ||||
|         L1 = get_landmarks(l_sights, SIGHTSEEING, coordinates=coordinates) | ||||
|         L1 = get_landmarks(amenity_selectors['sightseeing'], SIGHTSEEING, coordinates, parameters) | ||||
|         correct_score(L1, preferences.sightseeing) | ||||
|         L += L1 | ||||
|      | ||||
|     # List for nature | ||||
|     if preferences.nature.score != 0 : | ||||
|         L2 = get_landmarks(l_nature, NATURE, coordinates=coordinates) | ||||
|         L2 = get_landmarks(amenity_selectors['nature'], NATURE, coordinates, parameters) | ||||
|         correct_score(L2, preferences.nature) | ||||
|         L += L2 | ||||
|      | ||||
|     # List for shopping | ||||
|     if preferences.shopping.score != 0 : | ||||
|         L3 = get_landmarks(l_shop, SHOPPING, coordinates=coordinates) | ||||
|         L3 = get_landmarks(amenity_selectors['shopping'], SHOPPING, coordinates, parameters) | ||||
|         correct_score(L3, preferences.shopping) | ||||
|         L += L3 | ||||
|  | ||||
|     L = remove_duplicates(L) | ||||
|  | ||||
|     return L, take_most_important(L) | ||||
|     return L, take_most_important(L, parameters) | ||||
|  | ||||
|  | ||||
| """def generate_landmarks(preferences: Preferences, city_country: str = None, coordinates: Tuple[float, float] = None) -> Tuple[List[Landmark], List[Landmark]] : | ||||
| @@ -69,37 +77,8 @@ def generate_landmarks(preferences: Preferences, coordinates: Tuple[float, float | ||||
|     return remove_duplicates(L), take_most_important(L) | ||||
| """ | ||||
| # Helper function to gather the amenities list | ||||
| def get_amenities() -> List[List[str]] : | ||||
|      | ||||
|     # Get the list of amenities from the files | ||||
|     sightseeing = get_list('/amenities/sightseeing.am') | ||||
|     nature = get_list('/amenities/nature.am') | ||||
|     shopping = get_list('/amenities/shopping.am') | ||||
|  | ||||
|     return sightseeing, nature, shopping | ||||
|  | ||||
|  | ||||
| # Helper function to read a .am file and generate the corresponding list | ||||
| def get_list(path: str) -> List[str] : | ||||
|  | ||||
|     with open(os.path.dirname(os.path.abspath(__file__)) + path) as f : | ||||
|         content = f.readlines() | ||||
|  | ||||
|         amenities = [] | ||||
|         for line in content : | ||||
|             amenities.append(line.strip('\n')) | ||||
|  | ||||
|     return amenities | ||||
|  | ||||
|  | ||||
| # Take the most important landmarks from the list | ||||
| def take_most_important(L: List[Landmark], N = 0) -> List[Landmark] : | ||||
|      | ||||
|     # Read the parameters from the file | ||||
|     with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/landmarks_manager.params', "r") as f : | ||||
|         parameters = json.loads(f.read()) | ||||
|         N_important = parameters['N important'] | ||||
|      | ||||
| def take_most_important(L: List[Landmark], parameters: dict, N: int = 0) -> List[Landmark]: | ||||
|     L_copy = [] | ||||
|     L_clean = [] | ||||
|     scores = [0]*len(L) | ||||
| @@ -127,7 +106,8 @@ def take_most_important(L: List[Landmark], N = 0) -> List[Landmark] : | ||||
|     for i, elem in enumerate(L_copy) : | ||||
|         scores[i] = elem.attractiveness | ||||
|  | ||||
|     res = sorted(range(len(scores)), key = lambda sub: scores[sub])[-(N_important-N):] | ||||
|      | ||||
|     res = sorted(range(len(scores)), key = lambda sub: scores[sub])[-(parameters['N_important']-N):] | ||||
|  | ||||
|     for i, elem in enumerate(L_copy) : | ||||
|         if i in res : | ||||
| @@ -220,19 +200,25 @@ def create_bbox(coordinates: Tuple[float, float], side_length: int) -> Tuple[flo | ||||
|     return min_lat, min_lon, max_lat, max_lon | ||||
|  | ||||
|  | ||||
| def get_landmarks(list_amenity: list, landmarktype: LandmarkType, coordinates: Tuple[float, float]) -> List[Landmark] : | ||||
| def get_landmarks( | ||||
|     list_amenity: list, | ||||
|     landmarktype: LandmarkType, | ||||
|     coordinates: Tuple[float, float], | ||||
|     parameters: dict | ||||
| ) -> List[Landmark]: | ||||
|      | ||||
|     # Read the parameters from the file | ||||
|     with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/landmarks_manager.params', "r") as f : | ||||
|         parameters = json.loads(f.read()) | ||||
|         tag_coeff = parameters['tag coeff'] | ||||
|         park_coeff = parameters['park coeff'] | ||||
|         church_coeff = parameters['church coeff'] | ||||
|         radius = parameters['radius close to'] | ||||
|         bbox_side = parameters['city bbox side'] | ||||
|      | ||||
|     # # Read the parameters from the file | ||||
|     # with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/landmarks_manager.params', "r") as f : | ||||
|     #     parameters = json.loads(f.read()) | ||||
|     #     tag_coeff = parameters['tag coeff'] | ||||
|     #     park_coeff = parameters['park coeff'] | ||||
|     #     church_coeff = parameters['church coeff'] | ||||
|     #     radius = parameters['radius close to'] | ||||
|     #     bbox_side = parameters['city bbox side'] | ||||
|      | ||||
|     # Create bbox around start location | ||||
|     bbox = create_bbox(coordinates, bbox_side) | ||||
|     bbox = create_bbox(coordinates, parameters['city_bbox_side']) | ||||
|  | ||||
|     # Initialize some variables | ||||
|     N = 0 | ||||
| @@ -269,11 +255,11 @@ def get_landmarks(list_amenity: list, landmarktype: LandmarkType, coordinates: T | ||||
|                  | ||||
|                 # Add score of given landmark based on the number of surrounding elements. Penalty for churches as there are A LOT | ||||
|                 if amenity == "'amenity'='place_of_worship'" : | ||||
|                     score = int((count_elements_within_radius(location, radius) + n_tags*tag_coeff )*church_coeff)   | ||||
|                     score = int((count_elements_within_radius(location, parameters['radius_close_to']) + n_tags*parameters['tag_coeff'] )*parameters['church_coeff'])   | ||||
|                 elif amenity == "'leisure'='park'" : | ||||
|                     score = int((count_elements_within_radius(location, radius) + n_tags*tag_coeff )*park_coeff)   | ||||
|                     score = int((count_elements_within_radius(location, parameters['radius_close_to']) + n_tags*parameters['tag_coeff'] )*parameters['park_coeff'])   | ||||
|                 else : | ||||
|                     score = count_elements_within_radius(location, radius) + n_tags*tag_coeff | ||||
|                     score = count_elements_within_radius(location, parameters['radius_close_to']) + n_tags*parameters['tag_coeff'] | ||||
|  | ||||
|                 if score is not None : | ||||
|                     # Generate the landmark and append it to the list | ||||
|   | ||||
| @@ -1,23 +0,0 @@ | ||||
| import fastapi | ||||
| from dataclasses import dataclass | ||||
|  | ||||
|  | ||||
| @dataclass | ||||
| class Destination: | ||||
|     name: str | ||||
|     location: tuple | ||||
|     attractiveness: int | ||||
|  | ||||
|  | ||||
|  | ||||
| d = Destination() | ||||
|  | ||||
|  | ||||
|  | ||||
| def get_route() -> list[Destination]: | ||||
|     return {"route": "Hello World"} | ||||
|  | ||||
| endpoint = ("/get_route", get_route) | ||||
| end | ||||
| if __name__ == "__main__": | ||||
|     fastapi.run() | ||||
| @@ -1,13 +1,13 @@ | ||||
| import numpy as np | ||||
| import json, os | ||||
| import yaml | ||||
|  | ||||
| from typing import List, Tuple | ||||
| from scipy.optimize import linprog | ||||
| from math import radians, sin, cos, acos | ||||
| from shapely import Polygon | ||||
|  | ||||
| from structs.landmarks import Landmark | ||||
|  | ||||
| import constants | ||||
|      | ||||
| # Function to print the result | ||||
| def print_res(L: List[Landmark], L_tot): | ||||
| @@ -161,10 +161,11 @@ def get_distance(p1: Tuple[float, float], p2: Tuple[float, float], detour: float | ||||
| # We want to maximize the sightseeing :  max(c) st. A*x < b   and   A_eq*x = b_eq | ||||
| def init_ub_dist(landmarks: List[Landmark], max_steps: int): | ||||
|      | ||||
|     with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f : | ||||
|         parameters = json.loads(f.read()) | ||||
|         detour = parameters['detour factor'] | ||||
|         speed = parameters['average walking speed'] | ||||
|     # Read the parameters from the file | ||||
|     with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||
|         parameters = yaml.safe_load(f) | ||||
|         detour = parameters['detour_factor'] | ||||
|         speed = parameters['average_walking_speed'] | ||||
|      | ||||
|     # Objective function coefficients. a*x1 + b*x2 + c*x3 + ... | ||||
|     c = [] | ||||
| @@ -194,9 +195,9 @@ def respect_number(L:int, A_ub, b_ub): | ||||
|         b_ub.append(1) | ||||
|  | ||||
|     # Read the parameters from the file | ||||
|     with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f : | ||||
|         parameters = json.loads(f.read()) | ||||
|         max_landmarks = parameters['max landmarks'] | ||||
|     with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||
|         parameters = yaml.safe_load(f) | ||||
|         max_landmarks = parameters['max_landmarks'] | ||||
|  | ||||
|     A_ub = np.vstack((A_ub, ones*L)) | ||||
|     b_ub.append(max_landmarks+1) | ||||
| @@ -303,10 +304,11 @@ def respect_order(N: int, A_eq, b_eq): | ||||
| def link_list(order: List[int], landmarks: List[Landmark]) -> List[Landmark]: | ||||
|      | ||||
|     # Read the parameters from the file | ||||
|     with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f : | ||||
|         parameters = json.loads(f.read()) | ||||
|         detour_factor = parameters['detour factor'] | ||||
|         speed = parameters['average walking speed'] | ||||
|     with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||
|         parameters = yaml.safe_load(f) | ||||
|          | ||||
|     detour_factor = parameters['detour_factor'] | ||||
|     speed = parameters['average_walking_speed'] | ||||
|      | ||||
|     L =  [] | ||||
|     j = 0 | ||||
| @@ -329,10 +331,11 @@ def link_list(order: List[int], landmarks: List[Landmark])->List[Landmark] : | ||||
| def link_list_simple(ordered_visit: List[Landmark])-> List[Landmark] : | ||||
|      | ||||
|     # Read the parameters from the file | ||||
|     with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f : | ||||
|         parameters = json.loads(f.read()) | ||||
|         detour_factor = parameters['detour factor'] | ||||
|         speed = parameters['average walking speed'] | ||||
|     with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||
|         parameters = yaml.safe_load(f) | ||||
|          | ||||
|     detour_factor = parameters['detour_factor'] | ||||
|     speed = parameters['average_walking_speed'] | ||||
|  | ||||
|     L =  [] | ||||
|     j = 0 | ||||
|   | ||||
							
								
								
									
										26
									
								
								backend/src/parameters/amenity_selectors.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								backend/src/parameters/amenity_selectors.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| nature: | ||||
|   - "'leisure'='park'" | ||||
|   - "geological" | ||||
|   - "'natural'='geyser'" | ||||
|   - "'natural'='hot_spring'" | ||||
|   - "'natural'='arch'" | ||||
|   - "'natural'='volcano'" | ||||
|   - "'natural'='stone'" | ||||
|   - "'tourism'='alpine_hut'" | ||||
|   - "'tourism'='viewpoint'" | ||||
|   - "'tourism'='zoo'" | ||||
|   - "'waterway'='waterfall'" | ||||
|  | ||||
| shopping: | ||||
|   - "'shop'='department_store'" | ||||
|   - "'shop'='mall'" | ||||
|  | ||||
| sightseeing: | ||||
|   - "'tourism'='museum'" | ||||
|   - "'tourism'='attraction'" | ||||
|   - "'tourism'='gallery'" | ||||
|   - "historic" | ||||
|   - "'amenity'='planetarium'" | ||||
|   - "'amenity'='place_of_worship'" | ||||
|   - "'amenity'='fountain'" | ||||
|   - "'water'='reflecting_pool'" | ||||
							
								
								
									
										6
									
								
								backend/src/parameters/landmark_parameters.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								backend/src/parameters/landmark_parameters.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| city_bbox_side: 10 | ||||
| radius_close_to: 27.5 | ||||
| church_coeff: 0.6 | ||||
| park_coeff: 1.5 | ||||
| tag_coeff: 100 | ||||
| N_important: 40 | ||||
| @@ -1,8 +0,0 @@ | ||||
| { | ||||
|   "city bbox side" : 10, | ||||
|   "radius close to" : 27.5, | ||||
|   "church coeff" : 0.6, | ||||
|   "park coeff" : 1.5, | ||||
|   "tag coeff" : 100, | ||||
|   "N important" : 40 | ||||
| } | ||||
| @@ -1,5 +0,0 @@ | ||||
| { | ||||
|   "detour factor" : 1.4, | ||||
|   "average walking speed" : 4.8, | ||||
|   "max landmarks" : 10 | ||||
| } | ||||
							
								
								
									
										3
									
								
								backend/src/parameters/optimizer_parameters.yaml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								backend/src/parameters/optimizer_parameters.yaml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| detour_factor: 1.4 | ||||
| average_walking_speed: 4.8 | ||||
| max_landmarks: 10 | ||||
| @@ -1,7 +1,8 @@ | ||||
| from collections import defaultdict | ||||
| from heapq import heappop, heappush | ||||
| from itertools import permutations | ||||
| import os, json | ||||
| import os | ||||
| import yaml | ||||
|  | ||||
| from shapely import buffer, LineString, Point, Polygon, MultiPoint, convex_hull, concave_hull, LinearRing | ||||
| from typing import List, Tuple | ||||
| @@ -10,6 +11,7 @@ from math import pi | ||||
| from structs.landmarks import Landmark | ||||
| from landmarks_manager import take_most_important | ||||
| from optimizer import solve_optimization, link_list_simple, print_res, get_distance | ||||
| import constants | ||||
|  | ||||
|  | ||||
| def create_corridor(landmarks: List[Landmark], width: float) : | ||||
| @@ -122,12 +124,12 @@ def total_path_distance(path: List[Landmark], detour, speed) -> float: | ||||
|  | ||||
|  | ||||
| def find_shortest_path_through_all_landmarks(landmarks: List[Landmark]) -> List[Landmark]: | ||||
|   # Read the parameters from the file | ||||
|   with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||
|     parameters = yaml.safe_load(f) | ||||
|  | ||||
|   # Read from data | ||||
|   with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f : | ||||
|     parameters = json.loads(f.read()) | ||||
|     detour = parameters['detour factor'] | ||||
|     speed = parameters['average walking speed'] | ||||
|     detour = parameters['detour_factor'] | ||||
|     speed = parameters['average_walking_speed'] | ||||
|      | ||||
|   # Step 1: Find 'start' and 'finish' landmarks | ||||
|   start_idx = next(i for i, lm in enumerate(landmarks) if lm.name == 'start') | ||||
| @@ -175,7 +177,9 @@ def get_minor_landmarks(all_landmarks: List[Landmark], visited_landmarks: List[L | ||||
|     if is_in_area(area, landmark.location) and landmark.name not in visited_names: | ||||
|       second_order_landmarks.append(landmark) | ||||
|    | ||||
|   return take_most_important(second_order_landmarks, len(visited_landmarks)) | ||||
|   with constants.LANDMARK_PARAMETERS_PATH.open('r') as f: | ||||
|     parameters = yaml.safe_load(f) | ||||
|   return take_most_important(second_order_landmarks, parameters, len(visited_landmarks)) | ||||
|  | ||||
|  | ||||
|  | ||||
| @@ -195,10 +199,10 @@ def get_minor_landmarks(all_landmarks: List[Landmark], visited_landmarks: List[L | ||||
|  | ||||
| def refine_optimization(landmarks: List[Landmark], base_tour: List[Landmark], max_time: int, print_infos: bool) -> List[Landmark] : | ||||
|  | ||||
|   # Read from the file | ||||
|   with open (os.path.dirname(os.path.abspath(__file__)) + '/parameters/optimizer.params', "r") as f : | ||||
|     parameters = json.loads(f.read()) | ||||
|     max_landmarks = parameters['max landmarks'] | ||||
|   # Read the parameters from the file | ||||
|   with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||
|     parameters = yaml.safe_load(f) | ||||
|     max_landmarks = parameters['max_landmarks'] | ||||
|    | ||||
|   if len(base_tour)-2 >= max_landmarks : | ||||
|     return base_tour | ||||
|   | ||||
		Reference in New Issue
	
	Block a user