Files
thrillwiki_django_no_react/demo_roadtrip_usage.py

392 lines
12 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Demonstration script showing practical usage of the RoadTripService.
This script demonstrates real-world scenarios for using the OSM Road Trip Service
in the ThrillWiki application.
"""
from apps.parks.models import Park
from apps.parks.services import RoadTripService
import os
import django
# Setup Django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "thrillwiki.settings")
django.setup()
# New small helpers and constant to simplify functions and avoid repeated literals
MAGIC_KINGDOM = "Magic Kingdom"
def _format_coords(coords):
"""
Return (lat, lon) tuple or None for a coords object/sequence.
Accepts objects with .latitude/.longitude or indexable (lat, lon).
"""
if not coords:
return None
lat = getattr(coords, "latitude", None)
lon = getattr(coords, "longitude", None)
if lat is not None and lon is not None:
return (lat, lon)
# Fallback to indexable
try:
return (coords[0], coords[1])
except Exception:
return None
def _print_route_summary(route, indent=" "):
"""Safely print route summary fields if route is present."""
if not route:
return
# Use attributes with fallback to dict keys if needed
formatted_distance = getattr(route, "formatted_distance", None) or route.get(
"formatted_distance", "N/A"
) if isinstance(route, dict) else getattr(route, "formatted_distance", "N/A")
formatted_duration = getattr(route, "formatted_duration", None) or route.get(
"formatted_duration", "N/A"
) if isinstance(route, dict) else getattr(route, "formatted_duration", "N/A")
print(f"{indent}{formatted_distance}, {formatted_duration}")
def demo_florida_theme_park_trip():
"""
Demonstrate planning a Florida theme park road trip.
"""
print("🏖️ Florida Theme Park Road Trip Planner")
print("=" * 50)
service = RoadTripService()
# Define Florida theme parks with addresses
florida_parks = [
(MAGIC_KINGDOM, "Magic Kingdom Dr, Orlando, FL 32830"),
("Universal Studios Florida", "6000 Universal Blvd, Orlando, FL 32819"),
("SeaWorld Orlando", "7007 Sea World Dr, Orlando, FL 32821"),
("Busch Gardens Tampa", "10165 McKinley Dr, Tampa, FL 33612"),
]
print("Planning trip for these Florida parks:")
park_coords = {}
# small helper to geocode and store
def _geocode_and_store(name, address):
print(f"\n📍 Geocoding {name}...")
coords = service.geocode_address(address)
if coords:
latlon = _format_coords(coords)
if latlon:
park_coords[name] = coords
print(f" ✅ Located at {latlon[0]:.4f}, {latlon[1]:.4f}")
return True
print(f" ❌ Could not geocode {address}")
return False
for name, address in florida_parks:
_geocode_and_store(name, address)
if len(park_coords) < 2:
print("❌ Need at least 2 parks to plan a trip")
return
# Calculate distances between all parks
print("\n🗺️ Distance Matrix:")
park_names = list(park_coords.keys())
for i, park1 in enumerate(park_names):
for j, park2 in enumerate(park_names):
if i < j: # Only calculate each pair once
route = service.calculate_route(park_coords[park1], park_coords[park2])
if route:
print(f" {park1}{park2}")
_print_route_summary(route, indent=" ")
# Find central park for radiating searches
print(f"\n🎢 Parks within 100km of {MAGIC_KINGDOM}:")
magic_kingdom_coords = park_coords.get(MAGIC_KINGDOM)
if magic_kingdom_coords:
for name, coords in park_coords.items():
if name != MAGIC_KINGDOM:
route = service.calculate_route(magic_kingdom_coords, coords)
if route:
_print_route_summary(route, indent=f" {name}: ")
def demo_cross_country_road_trip():
"""
Demonstrate planning a cross-country theme park road trip.
"""
print("\n\n🇺🇸 Cross-Country Theme Park Road Trip")
print("=" * 50)
service = RoadTripService()
# Major theme parks across the US
major_parks = [
("Disneyland", "1313 Disneyland Dr, Anaheim, CA 92802"),
("Cedar Point", "1 Cedar Point Dr, Sandusky, OH 44870"),
("Six Flags Magic Mountain", "26101 Magic Mountain Pkwy, Valencia, CA 91355"),
("Walt Disney World", "Walt Disney World Resort, Orlando, FL 32830"),
]
print("Geocoding major US theme parks:")
park_coords = {}
for name, address in major_parks:
print(f"\n📍 {name}...")
coords = service.geocode_address(address)
if coords:
park_coords[name] = coords
latlon = _format_coords(coords)
if latlon:
print(f"{latlon[0]:.4f}, {latlon[1]:.4f}")
if len(park_coords) >= 3:
# Calculate an optimized route if we have DB parks
print("\n🛣️ Optimized Route Planning:")
print("Note: This would work with actual Park objects from the database")
# Show distances for a potential route
route_order = [
"Disneyland",
"Six Flags Magic Mountain",
"Cedar Point",
"Walt Disney World",
]
total_distance = 0
total_time = 0
for i in range(len(route_order) - 1):
from_park = route_order[i]
to_park = route_order[i + 1]
if from_park in park_coords and to_park in park_coords:
route = service.calculate_route(park_coords[from_park], park_coords[to_park])
if route:
total_distance += getattr(route, "distance_km", 0) or route.get("distance_km", 0) if isinstance(route, dict) else getattr(route, "distance_km", 0)
total_time += getattr(route, "duration_minutes", 0) or route.get("duration_minutes", 0) if isinstance(route, dict) else getattr(route, "duration_minutes", 0)
print(f" {i + 1}. {from_park}{to_park}")
_print_route_summary(route, indent=" ")
print("\n📊 Trip Summary:")
print(f" Total Distance: {total_distance:.1f}km")
hours = total_time // 60
mins = total_time % 60
print(f" Total Driving Time: {hours}h {mins}min")
# avoid division by zero
legs = max(1, len(route_order) - 1)
print(f" Average Distance per Leg: {total_distance / legs:.1f}km")
def demo_database_integration():
"""
Demonstrate working with actual parks from the database.
"""
print("\n\n🗄️ Database Integration Demo")
print("=" * 50)
service = RoadTripService()
# Get parks that have location data
parks_with_location = Park.objects.filter(location__point__isnull=False).select_related("location")[:5]
if not parks_with_location:
print("❌ No parks with location data found in database")
return
print(f"Found {len(parks_with_location)} parks with location data:")
for park in parks_with_location:
coords = getattr(park, "coordinates", None)
latlon = _format_coords(coords)
if latlon:
print(f" 🎢 {park.name}: {latlon[0]:.4f}, {latlon[1]:.4f}")
# Demonstrate nearby park search
if len(parks_with_location) >= 1:
center_park = parks_with_location[0]
print(f"\n🔍 Finding parks within 500km of {center_park.name}:")
nearby_parks = service.get_park_distances(center_park, radius_km=500)
if nearby_parks:
print(f" Found {len(nearby_parks)} nearby parks:")
for result in nearby_parks[:3]: # Show top 3
park = result.get("park") if isinstance(result, dict) else getattr(result, "park", None)
# use safe formatted strings
formatted_distance = result.get("formatted_distance", "N/A") if isinstance(result, dict) else getattr(result, "formatted_distance", "N/A")
formatted_duration = result.get("formatted_duration", "N/A") if isinstance(result, dict) else getattr(result, "formatted_duration", "N/A")
if park:
print(f" 📍 {park.name}: {formatted_distance} ({formatted_duration})")
else:
print(" No nearby parks found (may need larger radius)")
# Demonstrate multi-park trip planning
if len(parks_with_location) >= 3:
selected_parks = list(parks_with_location)[:3]
print("\n🗺️ Planning optimized trip for 3 parks:")
for park in selected_parks:
print(f" - {park.name}")
trip = service.create_multi_park_trip(selected_parks)
if trip:
print("\n✅ Optimized Route:")
print(f" Total Distance: {getattr(trip, 'formatted_total_distance', 'N/A')}")
print(f" Total Duration: {getattr(trip, 'formatted_total_duration', 'N/A')}")
print(" Route:")
for i, leg in enumerate(getattr(trip, "legs", []) or [], 1):
from_park = getattr(leg, "from_park", None)
to_park = getattr(leg, "to_park", None)
route = getattr(leg, "route", None)
if from_park and to_park:
print(f" {i}. {from_park.name}{to_park.name}")
_print_route_summary(route, indent=" ")
else:
print(" ❌ Could not optimize trip route")
def demo_geocoding_fallback():
"""
Demonstrate geocoding parks that don't have coordinates.
"""
print("\n\n🌍 Geocoding Demo")
print("=" * 50)
service = RoadTripService()
# Get parks without location data
parks_without_coords = Park.objects.filter(location__point__isnull=True).select_related("location")[:3]
if not parks_without_coords:
print("✅ All parks already have coordinates")
return
print(f"Found {len(parks_without_coords)} parks without coordinates:")
for park in parks_without_coords:
print(f"\n🎢 {park.name}")
location = getattr(park, "location", None)
if location:
# use getattr to avoid attribute errors
address_parts = [
getattr(park, "name", None),
getattr(location, "street_address", None),
getattr(location, "city", None),
getattr(location, "state", None),
getattr(location, "country", None),
]
address = ", ".join(part for part in address_parts if part)
print(f" Address: {address}")
# Try to geocode
success = service.geocode_park_if_needed(park)
if success:
coords = getattr(park, "coordinates", None)
latlon = _format_coords(coords)
if latlon:
print(f" ✅ Geocoded to: {latlon[0]:.4f}, {latlon[1]:.4f}")
else:
print(" ✅ Geocoded but coordinates unavailable")
else:
print(" ❌ Geocoding failed")
else:
print(" ❌ No location data available")
def demo_cache_performance():
"""
Demonstrate caching performance benefits.
"""
print("\n\n⚡ Cache Performance Demo")
print("=" * 50)
service = RoadTripService()
import time
# Test address for geocoding
test_address = "Disneyland, Anaheim, CA"
print(f"Testing cache performance with: {test_address}")
# First request (cache miss)
print("\n1⃣ First request (cache miss):")
start_time = time.time()
coords1 = service.geocode_address(test_address)
first_duration = time.time() - start_time
if coords1:
latlon = _format_coords(coords1)
if latlon:
print(f" ✅ Result: {latlon[0]:.4f}, {latlon[1]:.4f}")
else:
print(" ✅ Result obtained")
print(f" ⏱️ Duration: {first_duration:.2f} seconds")
# Second request (cache hit)
print("\n2⃣ Second request (cache hit):")
start_time = time.time()
coords2 = service.geocode_address(test_address)
second_duration = time.time() - start_time
if coords2:
latlon2 = _format_coords(coords2)
if latlon2:
print(f" ✅ Result: {latlon2[0]:.4f}, {latlon2[1]:.4f}")
else:
print(" ✅ Result obtained")
print(f" ⏱️ Duration: {second_duration:.2f} seconds")
if first_duration > second_duration and second_duration > 0:
speedup = first_duration / second_duration
print(f" 🚀 Cache speedup: {speedup:.1f}x faster")
# Compare coordinates if both present
if coords1 and coords2:
latlon1 = _format_coords(coords1)
latlon2 = _format_coords(coords2)
if latlon1 and latlon2 and latlon1 == latlon2:
print(" ✅ Results identical (cache working)")
def main():
"""
Run all demonstration scenarios.
"""
print("🎢 ThrillWiki Road Trip Service Demo")
print("This demo shows practical usage scenarios for the OSM Road Trip Service")
try:
demo_florida_theme_park_trip()
demo_cross_country_road_trip()
demo_database_integration()
demo_geocoding_fallback()
demo_cache_performance()
print("\n" + "=" * 50)
print("🎉 Demo completed successfully!")
print("\nThe Road Trip Service is ready for integration into ThrillWiki!")
print("\nKey Features Demonstrated:")
print("✅ Geocoding theme park addresses")
print("✅ Route calculation with distance/time")
print("✅ Multi-park trip optimization")
print("✅ Database integration with Park models")
print("✅ Caching for performance")
print("✅ Rate limiting for OSM compliance")
print("✅ Error handling and fallbacks")
except Exception as e:
print(f"\n❌ Demo failed with error: {e}")
import traceback
traceback.print_exc()
if __name__ == "__main__":
main()