Compare commits
	
		
			1 Commits
		
	
	
		
			v0.0.31
			...
			c612bf13f5
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c612bf13f5 | 
| @@ -29,9 +29,11 @@ def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[fl | |||||||
|     :return: the uuid of the first landmark in the optimized route |     :return: the uuid of the first landmark in the optimized route | ||||||
|     ''' |     ''' | ||||||
|     if preferences is None: |     if preferences is None: | ||||||
|         raise ValueError("Please provide preferences in the form of a 'Preference' BaseModel class.") |         raise HTTPException(status_code=406, detail="Preferences not provided") | ||||||
|  |     if preferences.shopping.score == 0 and preferences.culture.score == 0 and preferences.nature.score == 0: | ||||||
|  |         raise HTTPException(status_code=406, detail="All preferences are 0.") | ||||||
|     if start is None: |     if start is None: | ||||||
|         raise ValueError("Please provide the starting coordinates as a tuple of floats.") |         raise HTTPException(status_code=406, detail="Start coordinates not provided") | ||||||
|     if end is None: |     if end is None: | ||||||
|         end = start |         end = start | ||||||
|         logger.info("No end coordinates provided. Using start=end.") |         logger.info("No end coordinates provided. Using start=end.") | ||||||
| @@ -50,7 +52,12 @@ def new_trip(preferences: Preferences, start: tuple[float, float], end: tuple[fl | |||||||
|     landmarks_short.append(end_landmark) |     landmarks_short.append(end_landmark) | ||||||
|      |      | ||||||
|     # First stage optimization |     # First stage optimization | ||||||
|     base_tour = optimizer.solve_optimization(preferences.max_time_minute, landmarks_short) |     try: | ||||||
|  |         base_tour = optimizer.solve_optimization(preferences.max_time_minute, landmarks_short) | ||||||
|  |     except ArithmeticError: | ||||||
|  |         raise HTTPException(status_code=500, detail="No solution found") | ||||||
|  |     except TimeoutError: | ||||||
|  |         raise HTTPException(status_code=500, detail="Optimzation took too long") | ||||||
|      |      | ||||||
|     # Second stage optimization |     # 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) | ||||||
|   | |||||||
| @@ -1,6 +1,6 @@ | |||||||
| city_bbox_side: 5000 #m | city_bbox_side: 5000 #m | ||||||
| radius_close_to: 50 | radius_close_to: 50 | ||||||
| church_coeff: 0.8 | church_coeff: 0.8 | ||||||
| park_coeff: 1.2 | park_coeff: 1.0 | ||||||
| tag_coeff: 10 | tag_coeff: 10 | ||||||
| N_important: 40 | N_important: 40 | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ class LandmarkManager: | |||||||
|  |  | ||||||
|     logger = logging.getLogger(__name__) |     logger = logging.getLogger(__name__) | ||||||
|  |  | ||||||
|     city_bbox_side: int     # bbox side in meters |  | ||||||
|     radius_close_to: int    # radius in meters |     radius_close_to: int    # radius in meters | ||||||
|     church_coeff: float     # coeff to adjsut score of churches |     church_coeff: float     # coeff to adjsut score of churches | ||||||
|     park_coeff: float       # coeff to adjust score of parks |     park_coeff: float       # coeff to adjust score of parks | ||||||
| @@ -36,12 +35,17 @@ class LandmarkManager: | |||||||
|  |  | ||||||
|         with constants.LANDMARK_PARAMETERS_PATH.open('r') as f: |         with constants.LANDMARK_PARAMETERS_PATH.open('r') as f: | ||||||
|             parameters = yaml.safe_load(f) |             parameters = yaml.safe_load(f) | ||||||
|             self.city_bbox_side = parameters['city_bbox_side'] |             self.max_bbox_side = parameters['city_bbox_side'] | ||||||
|             self.radius_close_to = parameters['radius_close_to'] |             self.radius_close_to = parameters['radius_close_to'] | ||||||
|             self.church_coeff = parameters['church_coeff'] |             self.church_coeff = parameters['church_coeff'] | ||||||
|             self.park_coeff = parameters['park_coeff'] |             self.park_coeff = parameters['park_coeff'] | ||||||
|             self.tag_coeff = parameters['tag_coeff'] |             self.tag_coeff = parameters['tag_coeff'] | ||||||
|             self.N_important = parameters['N_important'] |             self.N_important = parameters['N_important'] | ||||||
|  |              | ||||||
|  |         with constants.OPTIMIZER_PARAMETERS_PATH.open('r') as f: | ||||||
|  |             parameters = yaml.safe_load(f) | ||||||
|  |             self.walking_speed = parameters['average_walking_speed'] | ||||||
|  |             self.detour_factor = parameters['detour_factor'] | ||||||
|  |  | ||||||
|         self.overpass = Overpass() |         self.overpass = Overpass() | ||||||
|         CachingStrategy.use(JSON, cacheDir=constants.OSM_CACHE_DIR) |         CachingStrategy.use(JSON, cacheDir=constants.OSM_CACHE_DIR) | ||||||
| @@ -65,23 +69,26 @@ class LandmarkManager: | |||||||
|                 - A list of the most important landmarks based on the user's preferences. |                 - A list of the most important landmarks based on the user's preferences. | ||||||
|         """ |         """ | ||||||
|  |  | ||||||
|  |         max_walk_dist = (preferences.max_time_minute/2)/60*self.walking_speed*1000/self.detour_factor | ||||||
|  |         reachable_bbox_side = min(max_walk_dist, self.max_bbox_side) | ||||||
|  |  | ||||||
|         L = [] |         L = [] | ||||||
|         bbox = self.create_bbox(center_coordinates) |         bbox = self.create_bbox(center_coordinates, reachable_bbox_side) | ||||||
|         # list for sightseeing |         # list for sightseeing | ||||||
|         if preferences.sightseeing.score != 0: |         if preferences.sightseeing.score != 0: | ||||||
|             score_function = lambda loc, n_tags: int((self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff) )*self.church_coeff)   |             score_function = lambda loc, n_tags: int((((n_tags**1.2)*self.tag_coeff) )*self.church_coeff)   # self.count_elements_close_to(loc) + | ||||||
|             L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], preferences.sightseeing.type, score_function) |             L1 = self.fetch_landmarks(bbox, self.amenity_selectors['sightseeing'], preferences.sightseeing.type, score_function) | ||||||
|             L += L1 |             L += L1 | ||||||
|  |  | ||||||
|         # list for nature |         # list for nature | ||||||
|         if preferences.nature.score != 0: |         if preferences.nature.score != 0: | ||||||
|             score_function = lambda loc, n_tags: int((self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff) )*self.park_coeff)   |             score_function = lambda loc, n_tags: int((((n_tags**1.2)*self.tag_coeff) )*self.park_coeff)   # self.count_elements_close_to(loc) + | ||||||
|             L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], preferences.nature.type, score_function) |             L2 = self.fetch_landmarks(bbox, self.amenity_selectors['nature'], preferences.nature.type, score_function) | ||||||
|             L += L2 |             L += L2 | ||||||
|  |  | ||||||
|         # list for shopping |         # list for shopping | ||||||
|         if preferences.shopping.score != 0: |         if preferences.shopping.score != 0: | ||||||
|             score_function = lambda loc, n_tags: int(self.count_elements_close_to(loc) + ((n_tags**1.2)*self.tag_coeff)) |             score_function = lambda loc, n_tags: int(((n_tags**1.2)*self.tag_coeff)) # self.count_elements_close_to(loc) + | ||||||
|             L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], preferences.shopping.type, score_function) |             L3 = self.fetch_landmarks(bbox, self.amenity_selectors['shopping'], preferences.shopping.type, score_function) | ||||||
|             L += L3 |             L += L3 | ||||||
|  |  | ||||||
| @@ -183,12 +190,13 @@ class LandmarkManager: | |||||||
|             return 0 |             return 0 | ||||||
|  |  | ||||||
|  |  | ||||||
|     def create_bbox(self, coordinates: tuple[float, float]) -> tuple[float, float, float, float]: |     def create_bbox(self, coordinates: tuple[float, float], reachable_bbox_side: int) -> tuple[float, float, float, float]: | ||||||
|         """ |         """ | ||||||
|         Create a bounding box around the given coordinates. |         Create a bounding box around the given coordinates. | ||||||
|  |  | ||||||
|         Args: |         Args: | ||||||
|             coordinates (tuple[float, float]): The latitude and longitude of the center of the bounding box. |             coordinates (tuple[float, float]): The latitude and longitude of the center of the bounding box. | ||||||
|  |             reachable_bbox_side (int): The side length of the bounding box in meters. | ||||||
|  |  | ||||||
|         Returns: |         Returns: | ||||||
|             tuple[float, float, float, float]: The minimum latitude, minimum longitude, maximum latitude, and maximum longitude |             tuple[float, float, float, float]: The minimum latitude, minimum longitude, maximum latitude, and maximum longitude | ||||||
| @@ -199,7 +207,7 @@ class LandmarkManager: | |||||||
|         lon = coordinates[1] |         lon = coordinates[1] | ||||||
|  |  | ||||||
|         # Half the side length in km (since it's a square bbox) |         # Half the side length in km (since it's a square bbox) | ||||||
|         half_side_length_km = self.city_bbox_side / 2 / 1000 |         half_side_length_km = reachable_bbox_side / 2 / 1000 | ||||||
|  |  | ||||||
|         # Convert distance to degrees |         # Convert distance to degrees | ||||||
|         lat_diff = half_side_length_km / 111  # 1 degree latitude is approximately 111 km |         lat_diff = half_side_length_km / 111  # 1 degree latitude is approximately 111 km | ||||||
| @@ -288,19 +296,24 @@ class LandmarkManager: | |||||||
|                         break |                         break | ||||||
|  |  | ||||||
|                     if "wikipedia" in tag: |                     if "wikipedia" in tag: | ||||||
|                         n_tags += 3             # wikipedia entries count more |                         n_tags += 1             # wikipedia entries count more | ||||||
|  |  | ||||||
|                     if tag == "wikidata": |                     # if tag == "wikidata": | ||||||
|                         Q = elem.tag('wikidata') |                     #     Q = elem.tag('wikidata') | ||||||
|                         site = Site("wikidata", "wikidata") |                     #     site = Site("wikidata", "wikidata") | ||||||
|                         item = ItemPage(site, Q) |                     #     item = ItemPage(site, Q) | ||||||
|                         item.get() |                     #     item.get() | ||||||
|                         n_languages = len(item.labels) |                     #     n_languages = len(item.labels) | ||||||
|                         n_tags += n_languages/10 |                     #     n_tags += n_languages/10 | ||||||
|  |                     if "viewpoint" in tag: | ||||||
|  |                         n_tags += 10 | ||||||
|  |  | ||||||
|                     if elem_type != "nature": |                     if elem_type != "nature": | ||||||
|                         if "leisure" in tag and elem.tag('leisure') == "park": |                         if "leisure" in tag and elem.tag('leisure') == "park": | ||||||
|                             elem_type = "nature" |                             elem_type = "nature" | ||||||
|  |                      | ||||||
|  |                     if elem_type == "nature": | ||||||
|  |                         n_tags += 1  | ||||||
|  |  | ||||||
|                     if landmarktype != "shopping": |                     if landmarktype != "shopping": | ||||||
|                         if "shop" in tag: |                         if "shop" in tag: | ||||||
| @@ -310,7 +323,6 @@ class LandmarkManager: | |||||||
|                         if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']: |                         if tag == "building" and elem.tag('building') in ['retail', 'supermarket', 'parking']: | ||||||
|                             skip = True |                             skip = True | ||||||
|                             break |                             break | ||||||
|  |  | ||||||
|                 if skip: |                 if skip: | ||||||
|                     continue |                     continue | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,4 +1,4 @@ | |||||||
| const String APP_NAME = 'AnyWay'; | const String APP_NAME = 'AnyWay'; | ||||||
|  |  | ||||||
| const String API_URL_BASE = 'https://anyway.kluster.moll.re'; | String API_URL_BASE = 'https://anyway.kluster.moll.re'; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,3 +1,5 @@ | |||||||
|  | import 'dart:developer'; | ||||||
|  |  | ||||||
| import 'package:anyway/structs/trip.dart'; | import 'package:anyway/structs/trip.dart'; | ||||||
| import 'package:auto_size_text/auto_size_text.dart'; | import 'package:auto_size_text/auto_size_text.dart'; | ||||||
|  |  | ||||||
| @@ -15,12 +17,15 @@ class Greeter extends StatefulWidget { | |||||||
| } | } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
| class _GreeterState extends State<Greeter> { | class _GreeterState extends State<Greeter> { | ||||||
|  |    | ||||||
|   Widget greeterBuilder (BuildContext context, Widget? child) { |   Widget greeterBuilder (BuildContext context, Widget? child) { | ||||||
|     ThemeData theme = Theme.of(context); |     ThemeData theme = Theme.of(context); | ||||||
|  |     TextStyle greeterStyle = TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24); | ||||||
|  |  | ||||||
|     Widget topGreeter; |     Widget topGreeter; | ||||||
|     if (widget.trip.landmarks.length > 1) { |  | ||||||
|  |     if (widget.trip.uuid != 'pending') { | ||||||
|       topGreeter = FutureBuilder( |       topGreeter = FutureBuilder( | ||||||
|         future: widget.trip.cityName, |         future: widget.trip.cityName, | ||||||
|         builder: (BuildContext context, AsyncSnapshot<String> snapshot) { |         builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||||
| @@ -28,17 +33,20 @@ class _GreeterState extends State<Greeter> { | |||||||
|             return AutoSizeText( |             return AutoSizeText( | ||||||
|               maxLines: 1, |               maxLines: 1, | ||||||
|               'Welcome to ${snapshot.data}!', |               'Welcome to ${snapshot.data}!', | ||||||
|               style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), |               style: greeterStyle | ||||||
|             ); |             ); | ||||||
|           } else if (snapshot.hasError) { |           } else if (snapshot.hasError) { | ||||||
|             return const AutoSizeText( |             log('Error while fetching city name'); | ||||||
|  |             return AutoSizeText( | ||||||
|               maxLines: 1, |               maxLines: 1, | ||||||
|               'Welcome to your trip!' |               'Welcome to your trip!', | ||||||
|  |               style: greeterStyle | ||||||
|             ); |             ); | ||||||
|           } else { |           } else { | ||||||
|             return const AutoSizeText( |             return AutoSizeText( | ||||||
|               maxLines: 1, |               maxLines: 1, | ||||||
|               'Welcome to ...' |               'Welcome to ...', | ||||||
|  |               style: greeterStyle | ||||||
|             ); |             ); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
| @@ -54,14 +62,24 @@ class _GreeterState extends State<Greeter> { | |||||||
|             future: widget.trip.cityName, |             future: widget.trip.cityName, | ||||||
|             builder: (BuildContext context, AsyncSnapshot<String> snapshot) { |             builder: (BuildContext context, AsyncSnapshot<String> snapshot) { | ||||||
|               if (snapshot.hasData) { |               if (snapshot.hasData) { | ||||||
|                 return Text( |                 return AutoSizeText( | ||||||
|  |                   maxLines: 1, | ||||||
|                   'Generating your trip to ${snapshot.data}...', |                   'Generating your trip to ${snapshot.data}...', | ||||||
|                   style: TextStyle(color: theme.primaryColor, fontWeight: FontWeight.bold, fontSize: 24), |                   style: greeterStyle | ||||||
|                 ); |                 ); | ||||||
|               } else if (snapshot.hasError) { |               } else if (snapshot.hasError) { | ||||||
|                 return const Text('Error while fetching city name'); |                 // the exact error is shown in the central part of the trip overview. No need to show it here | ||||||
|  |                 return AutoSizeText( | ||||||
|  |                   maxLines: 1, | ||||||
|  |                   'Error while loading trip.', | ||||||
|  |                   style: greeterStyle | ||||||
|  |                   ); | ||||||
|               } |               } | ||||||
|               return const Text('Generating your trip...'); |               return AutoSizeText( | ||||||
|  |                   maxLines: 1, | ||||||
|  |                   'Generating your trip...', | ||||||
|  |                   style: greeterStyle | ||||||
|  |                   ); | ||||||
|             } |             } | ||||||
|           ), |           ), | ||||||
|           Padding( |           Padding( | ||||||
|   | |||||||
| @@ -1,4 +1,3 @@ | |||||||
| import 'dart:collection'; |  | ||||||
| import 'dart:developer'; | import 'dart:developer'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
| import 'package:shared_preferences/shared_preferences.dart'; | import 'package:shared_preferences/shared_preferences.dart'; | ||||||
| @@ -19,32 +18,19 @@ class LandmarksOverview extends StatefulWidget { | |||||||
| } | } | ||||||
|  |  | ||||||
| class _LandmarksOverviewState extends State<LandmarksOverview> { | class _LandmarksOverviewState extends State<LandmarksOverview> { | ||||||
|   // final Future<List<Landmark>> _landmarks = fetchLandmarks(); |  | ||||||
|  |  | ||||||
|   @override |   @override | ||||||
|   Widget build(BuildContext context) { |   Widget build(BuildContext context) { | ||||||
|     return ListenableBuilder(//<LinkedList<Landmark>> |     return ListenableBuilder( | ||||||
|       listenable: widget.trip!, |       listenable: widget.trip!, | ||||||
|       builder: (BuildContext context, Widget? child) { |       builder: (BuildContext context, Widget? child) { | ||||||
|         Trip trip = widget.trip!; |         Trip trip = widget.trip!; | ||||||
|         log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); |         log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); | ||||||
|  |          | ||||||
|         List<Widget> children; |         List<Widget> children; | ||||||
|         if (trip.uuid == 'pending') { |          | ||||||
|           // the trip is still being fetched from the api |         if (trip.uuid != 'pending' && trip.uuid != 'error') { | ||||||
|           children = [Center(child: CircularProgressIndicator())]; |           log("Trip ${trip.uuid} ${trip.landmarks.length} landmarks"); | ||||||
|         } else if (trip.uuid == 'error') { |  | ||||||
|             children = [ |  | ||||||
|               const Icon( |  | ||||||
|                 Icons.error_outline, |  | ||||||
|                 color: Colors.red, |  | ||||||
|                 size: 60, |  | ||||||
|               ), |  | ||||||
|               Padding( |  | ||||||
|                 padding: const EdgeInsets.only(top: 16), |  | ||||||
|                 child: Text('Error: ${trip.cityName}'), |  | ||||||
|               ), |  | ||||||
|             ]; |  | ||||||
|         } else { |  | ||||||
|           if (trip.landmarks.length <= 1) { |           if (trip.landmarks.length <= 1) { | ||||||
|             children = [ |             children = [ | ||||||
|               const Text("No landmarks in this trip"), |               const Text("No landmarks in this trip"), | ||||||
| @@ -55,7 +41,26 @@ class _LandmarksOverviewState extends State<LandmarksOverview> { | |||||||
|               saveButton(), |               saveButton(), | ||||||
|             ]; |             ]; | ||||||
|           } |           } | ||||||
|  |         } else if(trip.uuid == 'pending') { | ||||||
|  |           // the trip is still being fetched from the api | ||||||
|  |           children = [Center(child: CircularProgressIndicator())]; | ||||||
|  |         } else { | ||||||
|  |             // trip.uuid == 'error' | ||||||
|  |             // show the error raised by the api | ||||||
|  |             // String error =  | ||||||
|  |             children = [ | ||||||
|  |               const Icon( | ||||||
|  |                 Icons.error_outline, | ||||||
|  |                 color: Colors.red, | ||||||
|  |                 size: 60, | ||||||
|  |               ), | ||||||
|  |               Padding( | ||||||
|  |                 padding: const EdgeInsets.only(top: 16), | ||||||
|  |                 child: Text('Error: ${trip.errorDescription}'), | ||||||
|  |               ), | ||||||
|  |             ]; | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return Column( |         return Column( | ||||||
|           children: children, |           children: children, | ||||||
|         ); |         ); | ||||||
| @@ -119,11 +124,6 @@ class _LandmarksOverviewState extends State<LandmarksOverview> { | |||||||
|  |  | ||||||
|  |  | ||||||
| Widget stepBetweenLandmarks(Landmark current, Landmark next) { | Widget stepBetweenLandmarks(Landmark current, Landmark next) { | ||||||
|   // This is a simple widget that draws a line between landmark-cards |  | ||||||
|   // It's a vertical dotted line |  | ||||||
|   // Next to the line is the icon for the mode of transport (walking for now) and the estimated time |  | ||||||
|   // There is also a button to open the navigation instructions as a new intent |  | ||||||
|   // next landmark is not actually required, but it ensures that the widget is deleted when the next landmark is removed (which makes sense, because then there will be another step) |  | ||||||
|   int timeRounded = 5 * (current.tripTime?.inMinutes ?? 0) ~/ 5; |   int timeRounded = 5 * (current.tripTime?.inMinutes ?? 0) ~/ 5; | ||||||
|   // ~/ is integer division (rounding) |   // ~/ is integer division (rounding) | ||||||
|   return Container( |   return Container( | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | import 'package:anyway/constants.dart'; | ||||||
| import 'package:anyway/structs/preferences.dart'; | import 'package:anyway/structs/preferences.dart'; | ||||||
| import 'package:flutter/material.dart'; | import 'package:flutter/material.dart'; | ||||||
|  |  | ||||||
| @@ -24,6 +25,32 @@ class _ProfilePageState extends State<ProfilePage> { | |||||||
|             onChanged: (bool? newValue) { |             onChanged: (bool? newValue) { | ||||||
|               setState(() { |               setState(() { | ||||||
|                 debugMode = newValue!; |                 debugMode = newValue!; | ||||||
|  |                 showDialog( | ||||||
|  |                   context: context, | ||||||
|  |                   builder: (BuildContext context) { | ||||||
|  |                     return AlertDialog( | ||||||
|  |                       title: Text('Debug mode - custom API'), | ||||||
|  |                       content: TextField( | ||||||
|  |                         decoration: InputDecoration( | ||||||
|  |                           hintText: 'http://localhost:8000' | ||||||
|  |                         ), | ||||||
|  |                       onChanged: (value) { | ||||||
|  |                         setState(() { | ||||||
|  |                           API_URL_BASE = value; | ||||||
|  |                         }); | ||||||
|  |                       }, | ||||||
|  |                     ), | ||||||
|  |                     actions: [ | ||||||
|  |                       TextButton( | ||||||
|  |                         child: Text('OK'), | ||||||
|  |                         onPressed: () { | ||||||
|  |                           Navigator.of(context).pop(); | ||||||
|  |                         }, | ||||||
|  |                       ), | ||||||
|  |                     ], | ||||||
|  |                     ); | ||||||
|  |                   } | ||||||
|  |                 ); | ||||||
|               }); |               }); | ||||||
|             } |             } | ||||||
|           ) |           ) | ||||||
|   | |||||||
| @@ -14,6 +14,7 @@ class Trip with ChangeNotifier { | |||||||
|   int totalTime; |   int totalTime; | ||||||
|   LinkedList<Landmark> landmarks; |   LinkedList<Landmark> landmarks; | ||||||
|   // could be empty as well |   // could be empty as well | ||||||
|  |   String? errorDescription; | ||||||
|  |  | ||||||
|   Future<String> get cityName async { |   Future<String> get cityName async { | ||||||
|     List<double>? location = landmarks.firstOrNull?.location;  |     List<double>? location = landmarks.firstOrNull?.location;  | ||||||
| @@ -64,6 +65,11 @@ class Trip with ChangeNotifier { | |||||||
|     landmarks.remove(landmark); |     landmarks.remove(landmark); | ||||||
|     notifyListeners(); |     notifyListeners(); | ||||||
|   } |   } | ||||||
|  |    | ||||||
|  |   void updateError(String error) { | ||||||
|  |     errorDescription = error; | ||||||
|  |     notifyListeners(); | ||||||
|  |   } | ||||||
|  |  | ||||||
|   factory Trip.fromPrefs(SharedPreferences prefs, String uuid) { |   factory Trip.fromPrefs(SharedPreferences prefs, String uuid) { | ||||||
|     String? content = prefs.getString('trip_$uuid'); |     String? content = prefs.getString('trip_$uuid'); | ||||||
|   | |||||||
| @@ -11,9 +11,11 @@ import "package:anyway/structs/preferences.dart"; | |||||||
| Dio dio = Dio( | Dio dio = Dio( | ||||||
|     BaseOptions( |     BaseOptions( | ||||||
|       baseUrl: API_URL_BASE, |       baseUrl: API_URL_BASE, | ||||||
|       // baseUrl: 'http://localhost:8000', |  | ||||||
|       connectTimeout: const Duration(seconds: 5), |       connectTimeout: const Duration(seconds: 5), | ||||||
|       receiveTimeout: const Duration(seconds: 120), |       receiveTimeout: const Duration(seconds: 120), | ||||||
|  |       // also accept 500 errors, since we cannot rule out that the server is at fault. We still want to gracefully handle these errors | ||||||
|  |       validateStatus: (status) => status! <= 500, | ||||||
|  |       receiveDataWhenStatusError: true, | ||||||
|       // api is notoriously slow |       // api is notoriously slow | ||||||
|       // headers: { |       // headers: { | ||||||
|       //   HttpHeaders.userAgentHeader: 'dio', |       //   HttpHeaders.userAgentHeader: 'dio', | ||||||
| @@ -45,24 +47,20 @@ fetchTrip( | |||||||
|   // handle errors |   // handle errors | ||||||
|   if (response.statusCode != 200) { |   if (response.statusCode != 200) { | ||||||
|     trip.updateUUID("error"); |     trip.updateUUID("error"); | ||||||
|     throw Exception('Failed to load trip'); |     if (response.data["detail"] != null) { | ||||||
|   } |       trip.updateError(response.data["detail"]); | ||||||
|   if (response.data["error"] != null) { |       // throw Exception(response.data["detail"]); | ||||||
|     trip.updateUUID("error"); |     } | ||||||
|     throw Exception(response.data["error"]); |  | ||||||
|   } |   } | ||||||
|  |  | ||||||
|   log(response.data.toString()); |   log(response.data.toString()); | ||||||
|   Map<String, dynamic> json = response.data; |   Map<String, dynamic> json = response.data; | ||||||
|  |  | ||||||
|   // only fill in the trip "meta" data for now |   // only fill in the trip "meta" data for now | ||||||
|   trip.loadFromJson(json); |   trip.loadFromJson(json); | ||||||
|  |  | ||||||
|  |  | ||||||
|   // now fill the trip with landmarks |   // now fill the trip with landmarks | ||||||
|   // if (trip.landmarks.isNotEmpty) { |   // we are going to recreate ALL the landmarks from the information given by the api | ||||||
|   //   trip.landmarks.clear(); |  | ||||||
|   // } |  | ||||||
|   // we are going to recreate all the landmarks from the information given by the api |  | ||||||
|   trip.landmarks.remove(trip.landmarks.first); |   trip.landmarks.remove(trip.landmarks.first); | ||||||
|   String? nextUUID = json["first_landmark_uuid"]; |   String? nextUUID = json["first_landmark_uuid"]; | ||||||
|   while (nextUUID != null) { |   while (nextUUID != null) { | ||||||
| @@ -83,8 +81,8 @@ Future<(Landmark, String?)> fetchLandmark(String uuid) async { | |||||||
|   if (response.statusCode != 200) { |   if (response.statusCode != 200) { | ||||||
|     throw Exception('Failed to load landmark'); |     throw Exception('Failed to load landmark'); | ||||||
|   } |   } | ||||||
|   if (response.data["error"] != null) { |   if (response.data["detail"] != null) { | ||||||
|     throw Exception(response.data["error"]); |     throw Exception(response.data["detail"]); | ||||||
|   } |   } | ||||||
|   log(response.data.toString()); |   log(response.data.toString()); | ||||||
|   Map<String, dynamic> json = response.data; |   Map<String, dynamic> json = response.data; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user