feat: Implement initial schema and add various API, service, and management command enhancements across the application.

This commit is contained in:
pacnpal
2026-01-01 15:13:01 -05:00
parent c95f99ca10
commit b243b17af7
413 changed files with 11164 additions and 17433 deletions

View File

@@ -148,12 +148,8 @@ class RoadTripService:
# Configuration from Django settings
self.cache_timeout = getattr(settings, "ROADTRIP_CACHE_TIMEOUT", 3600 * 24)
self.route_cache_timeout = getattr(
settings, "ROADTRIP_ROUTE_CACHE_TIMEOUT", 3600 * 6
)
self.user_agent = getattr(
settings, "ROADTRIP_USER_AGENT", "ThrillWiki Road Trip Planner"
)
self.route_cache_timeout = getattr(settings, "ROADTRIP_ROUTE_CACHE_TIMEOUT", 3600 * 6)
self.user_agent = getattr(settings, "ROADTRIP_USER_AGENT", "ThrillWiki Road Trip Planner")
self.request_timeout = getattr(settings, "ROADTRIP_REQUEST_TIMEOUT", 10)
self.max_retries = getattr(settings, "ROADTRIP_MAX_RETRIES", 3)
self.backoff_factor = getattr(settings, "ROADTRIP_BACKOFF_FACTOR", 2)
@@ -179,9 +175,7 @@ class RoadTripService:
for attempt in range(self.max_retries):
try:
response = self.session.get(
url, params=params, timeout=self.request_timeout
)
response = self.session.get(url, params=params, timeout=self.request_timeout)
response.raise_for_status()
return response.json()
@@ -192,9 +186,7 @@ class RoadTripService:
wait_time = self.backoff_factor**attempt
time.sleep(wait_time)
else:
raise OSMAPIException(
f"Failed to make request after {self.max_retries} attempts: {e}"
)
raise OSMAPIException(f"Failed to make request after {self.max_retries} attempts: {e}") from e
def geocode_address(self, address: str) -> Coordinates | None:
"""
@@ -243,9 +235,7 @@ class RoadTripService:
self.cache_timeout,
)
logger.info(
f"Geocoded '{address}' to {coords.latitude}, {coords.longitude}"
)
logger.info(f"Geocoded '{address}' to {coords.latitude}, {coords.longitude}")
return coords
else:
logger.warning(f"No geocoding results for address: {address}")
@@ -255,9 +245,7 @@ class RoadTripService:
logger.error(f"Geocoding failed for '{address}': {e}")
return None
def calculate_route(
self, start_coords: Coordinates, end_coords: Coordinates
) -> RouteInfo | None:
def calculate_route(self, start_coords: Coordinates, end_coords: Coordinates) -> RouteInfo | None:
"""
Calculate route between two coordinate points using OSRM.
@@ -327,9 +315,7 @@ class RoadTripService:
return route_info
else:
# Fallback to straight-line distance calculation
logger.warning(
"OSRM routing failed, falling back to straight-line distance"
)
logger.warning("OSRM routing failed, falling back to straight-line distance")
return self._calculate_straight_line_route(start_coords, end_coords)
except Exception as e:
@@ -337,9 +323,7 @@ class RoadTripService:
# Fallback to straight-line distance
return self._calculate_straight_line_route(start_coords, end_coords)
def _calculate_straight_line_route(
self, start_coords: Coordinates, end_coords: Coordinates
) -> RouteInfo:
def _calculate_straight_line_route(self, start_coords: Coordinates, end_coords: Coordinates) -> RouteInfo:
"""
Calculate straight-line distance as fallback when routing fails.
"""
@@ -356,10 +340,7 @@ class RoadTripService:
dlat = lat2 - lat1
dlon = lon2 - lon1
a = (
math.sin(dlat / 2) ** 2
+ math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
)
a = math.sin(dlat / 2) ** 2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon / 2) ** 2
c = 2 * math.asin(math.sqrt(a))
# Earth's radius in kilometers
@@ -376,9 +357,7 @@ class RoadTripService:
geometry=None,
)
def find_parks_along_route(
self, start_park: "Park", end_park: "Park", max_detour_km: float = 50
) -> list["Park"]:
def find_parks_along_route(self, start_park: "Park", end_park: "Park", max_detour_km: float = 50) -> list["Park"]:
"""
Find parks along a route within specified detour distance.
@@ -443,9 +422,7 @@ class RoadTripService:
return parks_along_route
def _calculate_detour_distance(
self, start: Coordinates, end: Coordinates, waypoint: Coordinates
) -> float | None:
def _calculate_detour_distance(self, start: Coordinates, end: Coordinates, waypoint: Coordinates) -> float | None:
"""
Calculate the detour distance when visiting a waypoint.
"""
@@ -508,9 +485,7 @@ class RoadTripService:
return best_trip
def _optimize_trip_nearest_neighbor(
self, park_list: list["Park"]
) -> RoadTrip | None:
def _optimize_trip_nearest_neighbor(self, park_list: list["Park"]) -> RoadTrip | None:
"""
Optimize trip using nearest neighbor heuristic (for larger lists).
"""
@@ -536,9 +511,7 @@ class RoadTripService:
if not park_coords:
continue
route = self.calculate_route(
Coordinates(*current_coords), Coordinates(*park_coords)
)
route = self.calculate_route(Coordinates(*current_coords), Coordinates(*park_coords))
if route and route.distance_km < min_distance:
min_distance = route.distance_km
@@ -553,9 +526,7 @@ class RoadTripService:
return self._create_trip_from_order(ordered_parks)
def _create_trip_from_order(
self, ordered_parks: list["Park"]
) -> RoadTrip | None:
def _create_trip_from_order(self, ordered_parks: list["Park"]) -> RoadTrip | None:
"""
Create a RoadTrip object from an ordered list of parks.
"""
@@ -576,9 +547,7 @@ class RoadTripService:
if not from_coords or not to_coords:
continue
route = self.calculate_route(
Coordinates(*from_coords), Coordinates(*to_coords)
)
route = self.calculate_route(Coordinates(*from_coords), Coordinates(*to_coords))
if route:
legs.append(TripLeg(from_park=from_park, to_park=to_park, route=route))
@@ -595,9 +564,7 @@ class RoadTripService:
total_duration_minutes=total_duration,
)
def get_park_distances(
self, center_park: "Park", radius_km: float = 100
) -> list[dict[str, Any]]:
def get_park_distances(self, center_park: "Park", radius_km: float = 100) -> list[dict[str, Any]]:
"""
Get all parks within radius of a center park with distances.
@@ -621,9 +588,7 @@ class RoadTripService:
search_distance = Distance(km=radius_km)
nearby_parks = (
Park.objects.filter(
location__point__distance_lte=(center_point, search_distance)
)
Park.objects.filter(location__point__distance_lte=(center_point, search_distance))
.exclude(id=center_park.id)
.select_related("location")
)
@@ -635,9 +600,7 @@ class RoadTripService:
if not park_coords:
continue
route = self.calculate_route(
Coordinates(*center_coords), Coordinates(*park_coords)
)
route = self.calculate_route(Coordinates(*center_coords), Coordinates(*park_coords))
if route:
results.append(
@@ -691,9 +654,7 @@ class RoadTripService:
if coords:
location.set_coordinates(coords.latitude, coords.longitude)
location.save()
logger.info(
f"Geocoded park '{park.name}' to {coords.latitude}, {coords.longitude}"
)
logger.info(f"Geocoded park '{park.name}' to {coords.latitude}, {coords.longitude}")
return True
return False