amazing cache #55
| @@ -328,7 +328,7 @@ div.media { | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1 id="title">Backend Testing Report</h1> | ||||
|     <p>Report generated on 28-Jan-2025 at 16:30:56 by <a href="https://pypi.python.org/pypi/pytest-html">pytest-html</a> | ||||
|     <p>Report generated on 29-Jan-2025 at 09:35:03 by <a href="https://pypi.python.org/pypi/pytest-html">pytest-html</a> | ||||
|         v4.1.1</p> | ||||
|     <div id="environment-header"> | ||||
|       <h2>Environment</h2> | ||||
| @@ -382,7 +382,7 @@ div.media { | ||||
|         <h2>Summary</h2> | ||||
|         <div class="additional-summary prefix"> | ||||
|         </div> | ||||
|         <p class="run-count">1 test took 00:00:10.</p> | ||||
|         <p class="run-count">1 test took 97 ms.</p> | ||||
|         <p class="filter">(Un)check the boxes to filter the results.</p> | ||||
|         <div class="summary__reload"> | ||||
|           <div class="summary__reload__button hidden" onclick="location.reload()"> | ||||
| @@ -432,7 +432,7 @@ div.media { | ||||
|     </table> | ||||
|   </body> | ||||
|   <footer> | ||||
|     <div id="data-container" data-jsonblob="{"environment": {"Python": "3.12.3", "Platform": "Linux-6.8.0-51-generic-x86_64-with-glibc2.39", "Packages": {"pytest": "8.3.4", "pluggy": "1.5.0"}, "Plugins": {"html": "4.1.1", "anyio": "4.8.0", "metadata": "3.1.1"}}, "tests": {"src/tests/test_main.py::test_turckheim": [{"extras": [], "result": "Passed", "testId": "src/tests/test_main.py::test_turckheim", "resultsTableRow": ["<td class=\"col-result\">Passed</td>", "<td class=\"col-testId\">src/tests/test_main.py::test_turckheim</td>", "<td>start (0 | 0) - 6 - H\u00f4tel des Deux-Clefs (217 | 5) - 1 - H\u00f4tel de ville (238 | 5) - 6 - finish (0 | 0) - 0</td>", "<td>23 min</td>", "<td>20 min</td>", "<td class=\"col-duration\">00:00:10</td>", "<td class=\"col-links\"></td>"], "log": "------------------------------ Captured log call -------------------------------\nDEBUG    asyncio:selector_events.py:64 Using selector: EpollSelector\nINFO     src.main:main.py:67 No end coordinates provided. Using start=end.\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:76 Starting to fetch landmarks...\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:88 Fetching sightseeing landmarks...\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:59 Cache miss for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:204 Fetched 20 landmarks of type sightseeing in (48.079458619727674, 7.272726663391359, 48.08971738027232, 7.288083336608641)\nINFO     src.utils.landmarks_manager:landmarks_manager.py:91 Found 20 sightseeing landmarks\nINFO     src.overpass.overpass:overpass.py:59 Cache miss for 1 quadrants.\nINFO     src.utils.cluster_manager:cluster_manager.py:145 Found 0 sightseeing clusters.\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:100 Fetching nature landmarks...\nINFO     src.overpass.overpass:overpass.py:59 Cache miss for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:59 Cache miss for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:59 Cache miss for 1 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 1 quadrants.\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:204 Fetched 10 landmarks of type nature in (48.079458619727674, 7.272726663391359, 48.08971738027232, 7.288083336608641)\nINFO     src.utils.landmarks_manager:landmarks_manager.py:103 Found 10 nature landmarks\nINFO     src.main:main.py:104 Fetched 22 landmarks in  \t: 5.367 seconds\nDEBUG    src.optimization.optimizer:optimizer.py:597 First results are out. Looking out for circles and correcting.\nINFO     src.optimization.optimizer:optimizer.py:637 Re-optimized 0 times, objective value : 455\nDEBUG    src.optimization.refiner:refiner.py:345 Using 5 minor landmarks around the predicted path\nDEBUG    src.optimization.optimizer:optimizer.py:597 First results are out. Looking out for circles and correcting.\nINFO     src.optimization.optimizer:optimizer.py:637 Re-optimized 0 times, objective value : 455\nDEBUG    src.main:main.py:130 First stage optimization\t: 0.126 seconds\nDEBUG    src.main:main.py:131 Second stage optimization\t: 0.033 seconds\nINFO     src.main:main.py:132 Total computation time\t: 0.159 seconds\nINFO     src.main:main.py:137 Generated a trip of 23 minutes with 4 landmarks in 5.526 seconds.\nDEBUG    src.overpass.overpass:overpass.py:112 Cache set for b2fd047a07f9563c8c0925aad9d61052\nDEBUG    src.overpass.overpass:overpass.py:112 Cache set for 7ee38297ba20e3bc47b984dce2785e22\nDEBUG    src.overpass.overpass:overpass.py:112 Cache set for 7f98015c7bc3a9100e10bc1c1ddfa572\nDEBUG    src.overpass.overpass:overpass.py:112 Cache set for 43424dbebba69d06a66a34728c3bb93e\nDEBUG    src.overpass.overpass:overpass.py:112 Cache set for b7d4ee874adac132bc9e80d7172fab8e\nINFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/trip/new &quot;HTTP/1.1 200 OK&quot;\n\n"}]}, "renderCollapsed": ["passed"], "initialSort": "result", "title": "Backend Testing Report"}"></div> | ||||
|     <div id="data-container" data-jsonblob="{"environment": {"Python": "3.12.3", "Platform": "Linux-6.8.0-51-generic-x86_64-with-glibc2.39", "Packages": {"pytest": "8.3.4", "pluggy": "1.5.0"}, "Plugins": {"html": "4.1.1", "anyio": "4.8.0", "metadata": "3.1.1"}}, "tests": {"src/tests/test_main.py::test_turckheim": [{"extras": [], "result": "Passed", "testId": "src/tests/test_main.py::test_turckheim", "resultsTableRow": ["<td class=\"col-result\">Passed</td>", "<td class=\"col-testId\">src/tests/test_main.py::test_turckheim</td>", "<td>start (0 | 0) - 3 - Mairie du 2e arrondissement (78 | 5) - 1 - Basilique Saint-Martin d'Ainay (406 | 5) - 3 - Chapelle Paul Couturier (109 | 5) - 1 - finish (0 | 0) - 0</td>", "<td>23 min</td>", "<td>20 min</td>", "<td class=\"col-duration\">97 ms</td>", "<td class=\"col-links\"></td>"], "log": "------------------------------ Captured log call -------------------------------\nDEBUG    asyncio:selector_events.py:64 Using selector: EpollSelector\nINFO     src.main:main.py:67 No end coordinates provided. Using start=end.\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:76 Starting to fetch landmarks...\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:88 Fetching sightseeing landmarks...\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nDEBUG    src.utils.landmarks_manager:landmarks_manager.py:204 Fetched 16 landmarks of type sightseeing in (45.74643460077641, 4.819803369760263, 45.756693361321055, 4.834505559895031)\nINFO     src.utils.landmarks_manager:landmarks_manager.py:91 Found 16 sightseeing landmarks\nINFO     src.overpass.overpass:overpass.py:55 Cache hit for 2/2 quadrants.\nINFO     src.utils.cluster_manager:cluster_manager.py:145 Found 0 sightseeing clusters.\nINFO     src.main:main.py:104 Fetched 12 landmarks in  \t: 0.011 seconds\nDEBUG    src.optimization.optimizer:optimizer.py:597 First results are out. Looking out for circles and correcting.\nINFO     src.optimization.optimizer:optimizer.py:637 Re-optimized 0 times, objective value : 593\nDEBUG    src.optimization.refiner:refiner.py:345 Using 2 minor landmarks around the predicted path\nDEBUG    src.optimization.optimizer:optimizer.py:597 First results are out. Looking out for circles and correcting.\nINFO     src.optimization.optimizer:optimizer.py:637 Re-optimized 0 times, objective value : 593\nDEBUG    src.main:main.py:130 First stage optimization\t: 0.045 seconds\nDEBUG    src.main:main.py:131 Second stage optimization\t: 0.025 seconds\nINFO     src.main:main.py:132 Total computation time\t: 0.071 seconds\nINFO     src.main:main.py:137 Generated a trip of 23 minutes with 5 landmarks in 0.082 seconds.\nINFO     httpx:_client.py:1025 HTTP Request: POST http://testserver/trip/new &quot;HTTP/1.1 200 OK&quot;\n\n"}]}, "renderCollapsed": ["passed"], "initialSort": "result", "title": "Backend Testing Report"}"></div> | ||||
|     <script> | ||||
|       (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){ | ||||
| const { getCollapsedCategory, setCollapsedIds } = require('./storage.js') | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
|  | ||||
| import os | ||||
| from pathlib import Path | ||||
| from typing import List, Literal | ||||
| from typing import List, Literal, Tuple | ||||
|  | ||||
|  | ||||
| LOCATION_PREFIX = Path('src') | ||||
| @@ -16,6 +16,7 @@ cache_dir_string = os.getenv('OSM_CACHE_DIR', './cache') | ||||
| OSM_CACHE_DIR = Path(cache_dir_string) | ||||
|  | ||||
| OSM_TYPES = List[Literal['way', 'node', 'relation']] | ||||
| BBOX = Tuple[float, float, float, float] | ||||
|  | ||||
| MEMCACHED_HOST_PATH = os.getenv('MEMCACHED_HOST_PATH', None) | ||||
| if MEMCACHED_HOST_PATH == "none": | ||||
|   | ||||
| @@ -54,7 +54,8 @@ class JSONCache(CachingStrategyBase): | ||||
|                 # Open and parse the cached JSON data | ||||
|                 with open(filename, 'r', encoding='utf-8') as file: | ||||
|                     data = json.load(file) | ||||
|                 return data  # Return the parsed JSON data | ||||
|                 # Return the data as a list of dicts. | ||||
|                 return data | ||||
|             except json.JSONDecodeError: | ||||
|                 return None  # Return None if parsing fails | ||||
|         return None | ||||
|   | ||||
| @@ -4,12 +4,14 @@ import urllib | ||||
| import math | ||||
| import logging | ||||
| import json | ||||
| from typing import List, Tuple | ||||
|  | ||||
| from .caching_strategy import get_cache_key, CachingStrategy | ||||
| from ..constants import OSM_CACHE_DIR, OSM_TYPES | ||||
| from ..constants import OSM_CACHE_DIR, OSM_TYPES, BBOX | ||||
|  | ||||
|  | ||||
| RESOLUTION = 0.05 | ||||
| CELL = Tuple[int, int] | ||||
|  | ||||
|  | ||||
| class Overpass : | ||||
| @@ -29,8 +31,8 @@ class Overpass : | ||||
|         self.caching_strategy = CachingStrategy.use(caching_strategy, cache_dir=cache_dir) | ||||
|  | ||||
|  | ||||
|     def send_query(self, bbox: tuple, osm_types: OSM_TYPES, | ||||
|                     selector: str, conditions: list=None, out='center'): | ||||
|     def send_query(self, bbox: BBOX, osm_types: OSM_TYPES, | ||||
|                     selector: str, conditions: list=None, out='center') -> List[dict]: | ||||
|         """ | ||||
|         Sends the Overpass QL query to the Overpass API and returns the parsed json response. | ||||
|  | ||||
| @@ -42,28 +44,35 @@ class Overpass : | ||||
|             out (str): Output format ('center', 'body', etc.). Defaults to 'center'. | ||||
|  | ||||
|         Returns: | ||||
|             dict:    Parsed json response from the Overpass API, or cached data if available. | ||||
|             list:   Parsed json response from the Overpass API, or cached data if available. | ||||
|         """ | ||||
|         # Determine which grid cells overlap with this bounding box. | ||||
|         overlapping_cells = Overpass._get_overlapping_cells(bbox) | ||||
|  | ||||
|         # Retrieve cached data and identify missing cache entries | ||||
|         cached_responses, hollow_cache_keys = self._retrieve_cached_data(overlapping_cells, osm_types, selector, conditions, out) | ||||
|         cached_responses, non_cached_cells = self._retrieve_cached_data(overlapping_cells, osm_types, selector, conditions, out) | ||||
|  | ||||
|         # If there is no missing data, return the cached responses | ||||
|         if not hollow_cache_keys : | ||||
|             self.logger.info(f'Cache hit for {len(cached_responses)} quadrants.') | ||||
|             return self._combine_cached_data(cached_responses) | ||||
|         self.logger.info(f'Cache hit for {len(overlapping_cells)-len(non_cached_cells)}/{len(overlapping_cells)} quadrants.') | ||||
|  | ||||
|         # TODO If there is SOME missing data : hybrid stuff with partial cache | ||||
|         self.logger.info(f'Cache miss for {len(hollow_cache_keys)} quadrants.') | ||||
|         # If there is no missing data, return the cached responses after filtering. | ||||
|         if not non_cached_cells : | ||||
|             return Overpass._filter_landmarks(cached_responses, bbox) | ||||
|  | ||||
|         # Missing data: Make a query to Overpass API | ||||
|         query_str = Overpass.build_query(bbox, osm_types, selector, conditions, out) | ||||
|         return self.fetch_data_from_api(query_str) | ||||
|         # If there is no cached data, fetch all from Overpass. | ||||
|         elif not cached_responses : | ||||
|             query_str = Overpass.build_query(bbox, osm_types, selector, conditions, out) | ||||
|             return self.fetch_data_from_api(query_str) | ||||
|  | ||||
|         # Hybrid cache: some data from Overpass, some data from cache. | ||||
|         else : | ||||
|             # Resize the bbox for smaller search area and build new query string. | ||||
|             non_cached_bbox = Overpass._get_non_cached_bbox(non_cached_cells, bbox) | ||||
|             query_str = Overpass.build_query(non_cached_bbox, osm_types, selector, conditions, out) | ||||
|             non_cached_responses = self.fetch_data_from_api(query_str) | ||||
|             return Overpass._filter_landmarks(cached_responses, bbox) + non_cached_responses | ||||
|  | ||||
|  | ||||
|     def fetch_data_from_api(self, query_str: str) -> list: | ||||
|     def fetch_data_from_api(self, query_str: str) -> List[dict]: | ||||
|         """ | ||||
|         Fetch data from the Overpass API and return the json data. | ||||
|  | ||||
| @@ -117,7 +126,7 @@ class Overpass : | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def build_query(bbox: tuple, osm_types: OSM_TYPES, | ||||
|     def build_query(bbox: BBOX, osm_types: OSM_TYPES, | ||||
|                     selector: str, conditions: list=None, out='center') -> str: | ||||
|         """ | ||||
|         Constructs a query string for the Overpass API to retrieve OpenStreetMap (OSM) data. | ||||
| @@ -160,9 +169,10 @@ class Overpass : | ||||
|         return query | ||||
|  | ||||
|  | ||||
|     def _retrieve_cached_data(self, overlapping_cells: list, osm_types: OSM_TYPES, selector: str, conditions: list, out: str): | ||||
|     def _retrieve_cached_data(self, overlapping_cells: CELL, osm_types: OSM_TYPES, | ||||
|                               selector: str, conditions: list, out: str) -> Tuple[List[dict], list[CELL]]: | ||||
|         """ | ||||
|         Retrieve cached data and identify missing cache entries. | ||||
|         Retrieve cached data and identify missing cache quadrants. | ||||
|  | ||||
|         Args: | ||||
|             overlapping_cells (list): Cells to check for cached data. | ||||
| @@ -174,7 +184,7 @@ class Overpass : | ||||
|         Returns: | ||||
|             tuple: A tuple containing: | ||||
|                 - cached_responses (list): List of cached data found. | ||||
|                 - hollow_cache_keys (list): List of keys with missing data. | ||||
|                 - non_cached_cells (list(tuple)): List of cells with missing data. | ||||
|         """ | ||||
|         cell_key_dict = {} | ||||
|         for cell in overlapping_cells : | ||||
| @@ -184,29 +194,29 @@ class Overpass : | ||||
|             cell_key_dict[cell] = get_cache_key(key_str) | ||||
|  | ||||
|         cached_responses = [] | ||||
|         hollow_cache_keys = [] | ||||
|         non_cached_cells = [] | ||||
|  | ||||
|         # Retrieve the cached data and mark the missing entries as hollow | ||||
|         for cell, key in cell_key_dict.items(): | ||||
|             cached_data = self.caching_strategy.get(key) | ||||
|             if cached_data is not None : | ||||
|                 cached_responses.append(cached_data) | ||||
|                 cached_responses += cached_data | ||||
|             else: | ||||
|                 self.caching_strategy.set_hollow(key, cell, osm_types, selector, conditions, out) | ||||
|                 hollow_cache_keys.append(key) | ||||
|                 non_cached_cells.append(cell) | ||||
|  | ||||
|         return cached_responses, hollow_cache_keys | ||||
|         return cached_responses, non_cached_cells | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def _build_query_from_hollow(json_data: dict): | ||||
|     def _build_query_from_hollow(json_data: dict) -> Tuple[str, str]: | ||||
|         """ | ||||
|         Build query string using information from a hollow cache entry. | ||||
|         """ | ||||
|         # Extract values from the JSON object | ||||
|         key = json_data.get('key') | ||||
|         cell = tuple(json_data.get('cell')) | ||||
|         bbox = Overpass._get_bbox_from_grid_cell(cell[0], cell[1]) | ||||
|         bbox = Overpass._get_bbox_from_grid_cell(cell) | ||||
|         osm_types = json_data.get('osm_types') | ||||
|         selector = json_data.get('selector') | ||||
|         conditions = json_data.get('conditions') | ||||
| @@ -218,7 +228,7 @@ class Overpass : | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def _get_overlapping_cells(query_bbox: tuple): | ||||
|     def _get_overlapping_cells(query_bbox: tuple) -> List[CELL]: | ||||
|         """ | ||||
|         Returns a set of all grid cells that overlap with the given bounding box. | ||||
|         """ | ||||
| @@ -237,7 +247,7 @@ class Overpass : | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def _get_grid_cell(lat: float, lon: float): | ||||
|     def _get_grid_cell(lat: float, lon: float) -> CELL: | ||||
|         """ | ||||
|         Returns the grid cell coordinates for a given latitude and longitude. | ||||
|         Each grid cell is 0.05°lat x 0.05°lon resolution in size. | ||||
| @@ -248,7 +258,7 @@ class Overpass : | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def _get_bbox_from_grid_cell(lat_index: int, lon_index: int): | ||||
|     def _get_bbox_from_grid_cell(cell: CELL) -> BBOX: | ||||
|         """ | ||||
|         Returns the bounding box for a given grid cell index. | ||||
|         Each grid cell is resolution x resolution in size. | ||||
| @@ -256,26 +266,84 @@ class Overpass : | ||||
|         The bounding box is returned as (min_lat, min_lon, max_lat, max_lon). | ||||
|         """ | ||||
|         # Calculate the southwest (min_lat, min_lon) corner of the bounding box | ||||
|         min_lat = round(lat_index * RESOLUTION, 2) | ||||
|         min_lon = round(lon_index * RESOLUTION, 2) | ||||
|         min_lat = round(cell[0] * RESOLUTION, 2) | ||||
|         min_lon = round(cell[1] * RESOLUTION, 2) | ||||
|  | ||||
|         # Calculate the northeast (max_lat, max_lon) corner of the bounding box | ||||
|         max_lat = round((lat_index + 1) * RESOLUTION, 2) | ||||
|         max_lon = round((lon_index + 1) * RESOLUTION, 2) | ||||
|         max_lat = round((cell[0] + 1) * RESOLUTION, 2) | ||||
|         max_lon = round((cell[1] + 1) * RESOLUTION, 2) | ||||
|  | ||||
|         return (min_lat, min_lon, max_lat, max_lon) | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def _combine_cached_data(cached_data_list): | ||||
|     def _get_non_cached_bbox(non_cached_cells: List[CELL], original_bbox: BBOX): | ||||
|         """ | ||||
|         Combines data from multiple cached responses into a single result. | ||||
|         Calculate the non-cached bounding box by excluding cached cells. | ||||
|  | ||||
|         Args: | ||||
|             non_cached_cells (list): The list of cells that were not found in the cache. | ||||
|             original_bbox (tuple): The original bounding box (min_lat, min_lon, max_lat, max_lon). | ||||
|  | ||||
|         Returns: | ||||
|             tuple: The new bounding box that excludes cached cells, or None if all cells are cached. | ||||
|         """ | ||||
|         combined_data = [] | ||||
|         for cached_data in cached_data_list: | ||||
|             for element in cached_data: | ||||
|                 combined_data.append(element) | ||||
|         return combined_data | ||||
|         if not non_cached_cells: | ||||
|             return None  # All cells were cached | ||||
|  | ||||
|         # Initialize the non-cached bounding box with extreme values | ||||
|         min_lat, min_lon, max_lat, max_lon = float('inf'), float('inf'), float('-inf'), float('-inf') | ||||
|  | ||||
|         # Iterate over non-cached cells to find the new bounding box | ||||
|         for cell in non_cached_cells: | ||||
|             cell_min_lat, cell_min_lon, cell_max_lat, cell_max_lon = Overpass._get_bbox_from_grid_cell(cell) | ||||
|  | ||||
|             min_lat = min(min_lat, cell_min_lat) | ||||
|             min_lon = min(min_lon, cell_min_lon) | ||||
|             max_lat = max(max_lat, cell_max_lat) | ||||
|             max_lon = max(max_lon, cell_max_lon) | ||||
|  | ||||
|         # If no update to bounding box, return the original | ||||
|         if min_lat == float('inf') or min_lon == float('inf'): | ||||
|             return None | ||||
|  | ||||
|         return (max(min_lat, original_bbox[0]),  | ||||
|                 max(min_lon, original_bbox[1]),  | ||||
|                 min(max_lat, original_bbox[2]),  | ||||
|                 min(max_lon, original_bbox[3])) | ||||
|  | ||||
|  | ||||
|     @staticmethod | ||||
|     def _filter_landmarks(elements: List[dict], bbox: BBOX) -> List[dict]: | ||||
|         """ | ||||
|         Filters elements based on whether their coordinates are inside the given bbox. | ||||
|  | ||||
|         Args: | ||||
|         - elements (list of dict): List of elements containing coordinates. | ||||
|         - bbox (tuple): A bounding box defined as (min_lat, min_lon, max_lat, max_lon). | ||||
|  | ||||
|         Returns: | ||||
|         - list: A list of elements whose coordinates are inside the bounding box. | ||||
|         """ | ||||
|  | ||||
|         filtered_elements = [] | ||||
|         min_lat, min_lon, max_lat, max_lon = bbox | ||||
|  | ||||
|         for elem in elements: | ||||
|             # Extract coordinates based on the 'type' of element | ||||
|             if elem.get('type') != 'node': | ||||
|                 center = elem.get('center', {}) | ||||
|                 lat = float(center.get('lat', 0)) | ||||
|                 lon = float(center.get('lon', 0)) | ||||
|             else: | ||||
|                 lat = float(elem.get('lat', 0)) | ||||
|                 lon = float(elem.get('lon', 0)) | ||||
|  | ||||
|             # Check if the coordinates fall within the given bounding box | ||||
|             if min_lat <= lat <= max_lat and min_lon <= lon <= max_lon: | ||||
|                 filtered_elements.append(elem) | ||||
|  | ||||
|         return filtered_elements | ||||
|  | ||||
|  | ||||
| def get_base_info(elem: dict, osm_type: OSM_TYPES, with_name=False) : | ||||
|   | ||||
| @@ -27,11 +27,13 @@ def test_turckheim(client, request):    # pylint: disable=redefined-outer-name | ||||
|         "/trip/new", | ||||
|         json={ | ||||
|             "preferences": {"sightseeing": {"type": "sightseeing", "score": 5}, | ||||
|             "nature": {"type": "nature", "score": 5}, | ||||
|             "nature": {"type": "nature", "score": 0}, | ||||
|             "shopping": {"type": "shopping", "score": 0}, | ||||
|             "max_time_minute": duration_minutes, | ||||
|             "detour_tolerance_minute": 0}, | ||||
|             "start": [48.084588, 7.280405] | ||||
|             # "start": [48.084588, 7.280405] | ||||
|             # "start": [45.74445023349939, 4.8222687890538865] | ||||
|             "start": [45.75156398104873, 4.827154464827647] | ||||
|             } | ||||
|         ) | ||||
|     result = response.json() | ||||
| @@ -56,7 +58,7 @@ def test_turckheim(client, request):    # pylint: disable=redefined-outer-name | ||||
|     assert duration_minutes*1.2 > result['total_time'], f"Trip too long: {result['total_time']} instead of {duration_minutes}" | ||||
|     # assert 2!= 3 | ||||
|  | ||||
|  | ||||
| ''' | ||||
| def test_bellecour(client, request) :   # pylint: disable=redefined-outer-name | ||||
|     """ | ||||
|     Test n°2 : Custom test in Lyon centre to ensure proper decision making in crowded area. | ||||
| @@ -342,3 +344,4 @@ def test_shopping(client, request) :   # pylint: disable=redefined-outer-name | ||||
|     assert comp_time < 30, f"Computation time exceeded 30 seconds: {comp_time:.2f} seconds" | ||||
|     assert duration_minutes*0.8 < result['total_time'], f"Trip too short: {result['total_time']} instead of {duration_minutes}" | ||||
|     assert duration_minutes*1.2 > result['total_time'], f"Trip too long: {result['total_time']} instead of {duration_minutes}" | ||||
| ''' | ||||
		Reference in New Issue
	
	Block a user