mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-23 01:51:09 -05:00
Add migrations for ParkPhoto and RidePhoto models with associated events
- Created ParkPhoto and ParkPhotoEvent models in the parks app, including fields for image, caption, alt text, and relationships to the Park model. - Implemented triggers for insert and update operations on ParkPhoto to log changes in ParkPhotoEvent. - Created RidePhoto and RidePhotoEvent models in the rides app, with similar structure and functionality as ParkPhoto. - Added fields for photo type in RidePhoto and implemented corresponding triggers for logging changes. - Established necessary indexes and unique constraints for both models to ensure data integrity and optimize queries.
This commit is contained in:
@@ -3,5 +3,11 @@ from .park_management import ParkService
|
||||
from .location_service import ParkLocationService
|
||||
from .filter_service import ParkFilterService
|
||||
from .media_service import ParkMediaService
|
||||
__all__ = ["RoadTripService", "ParkService",
|
||||
"ParkLocationService", "ParkFilterService", "ParkMediaService"]
|
||||
|
||||
__all__ = [
|
||||
"RoadTripService",
|
||||
"ParkService",
|
||||
"ParkLocationService",
|
||||
"ParkFilterService",
|
||||
"ParkMediaService",
|
||||
]
|
||||
|
||||
@@ -4,8 +4,7 @@ Handles geocoding, reverse geocoding, and location search for parks.
|
||||
"""
|
||||
|
||||
import requests
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
from django.conf import settings
|
||||
from typing import List, Dict, Any, Optional
|
||||
from django.core.cache import cache
|
||||
from django.db import transaction
|
||||
import logging
|
||||
|
||||
@@ -27,7 +27,7 @@ class ParkMediaService:
|
||||
caption: str = "",
|
||||
alt_text: str = "",
|
||||
is_primary: bool = False,
|
||||
auto_approve: bool = False
|
||||
auto_approve: bool = False,
|
||||
) -> ParkPhoto:
|
||||
"""
|
||||
Upload a photo for a park.
|
||||
@@ -64,7 +64,7 @@ class ParkMediaService:
|
||||
alt_text=alt_text,
|
||||
is_primary=is_primary,
|
||||
is_approved=auto_approve,
|
||||
uploaded_by=user
|
||||
uploaded_by=user,
|
||||
)
|
||||
|
||||
# Extract EXIF date
|
||||
@@ -77,9 +77,7 @@ class ParkMediaService:
|
||||
|
||||
@staticmethod
|
||||
def get_park_photos(
|
||||
park: Park,
|
||||
approved_only: bool = True,
|
||||
primary_first: bool = True
|
||||
park: Park, approved_only: bool = True, primary_first: bool = True
|
||||
) -> List[ParkPhoto]:
|
||||
"""
|
||||
Get photos for a park.
|
||||
@@ -98,9 +96,9 @@ class ParkMediaService:
|
||||
queryset = queryset.filter(is_approved=True)
|
||||
|
||||
if primary_first:
|
||||
queryset = queryset.order_by('-is_primary', '-created_at')
|
||||
queryset = queryset.order_by("-is_primary", "-created_at")
|
||||
else:
|
||||
queryset = queryset.order_by('-created_at')
|
||||
queryset = queryset.order_by("-created_at")
|
||||
|
||||
return list(queryset)
|
||||
|
||||
@@ -190,7 +188,8 @@ class ParkMediaService:
|
||||
photo.delete()
|
||||
|
||||
logger.info(
|
||||
f"Photo {photo_id} deleted from park {park_slug} by user {deleted_by.username}")
|
||||
f"Photo {photo_id} deleted from park {park_slug} by user {deleted_by.username}"
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete photo {photo.pk}: {str(e)}")
|
||||
@@ -214,7 +213,7 @@ class ParkMediaService:
|
||||
"approved_photos": photos.filter(is_approved=True).count(),
|
||||
"pending_photos": photos.filter(is_approved=False).count(),
|
||||
"has_primary": photos.filter(is_primary=True).exists(),
|
||||
"recent_uploads": photos.order_by('-created_at')[:5].count()
|
||||
"recent_uploads": photos.order_by("-created_at")[:5].count(),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
@@ -237,5 +236,6 @@ class ParkMediaService:
|
||||
approved_count += 1
|
||||
|
||||
logger.info(
|
||||
f"Bulk approved {approved_count} photos by user {approved_by.username}")
|
||||
f"Bulk approved {approved_count} photos by user {approved_by.username}"
|
||||
)
|
||||
return approved_count
|
||||
|
||||
@@ -192,8 +192,7 @@ class RoadTripService:
|
||||
time.sleep(wait_time)
|
||||
else:
|
||||
raise OSMAPIException(
|
||||
f"Failed to make request after {
|
||||
self.max_retries} attempts: {e}"
|
||||
f"Failed to make request after {self.max_retries} attempts: {e}"
|
||||
)
|
||||
|
||||
def geocode_address(self, address: str) -> Optional[Coordinates]:
|
||||
@@ -244,9 +243,7 @@ class RoadTripService:
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Geocoded '{address}' to {
|
||||
coords.latitude}, {
|
||||
coords.longitude}"
|
||||
f"Geocoded '{address}' to {coords.latitude}, {coords.longitude}"
|
||||
)
|
||||
return coords
|
||||
else:
|
||||
@@ -274,22 +271,18 @@ class RoadTripService:
|
||||
return None
|
||||
|
||||
# Check cache first
|
||||
cache_key = f"roadtrip:route:{
|
||||
start_coords.latitude},{
|
||||
start_coords.longitude}:{
|
||||
end_coords.latitude},{
|
||||
end_coords.longitude}"
|
||||
cache_key = f"roadtrip:route:{start_coords.latitude},{start_coords.longitude}:{
|
||||
end_coords.latitude
|
||||
},{end_coords.longitude}"
|
||||
cached_result = cache.get(cache_key)
|
||||
if cached_result:
|
||||
return RouteInfo(**cached_result)
|
||||
|
||||
try:
|
||||
# Format coordinates for OSRM (lon,lat format)
|
||||
coords_string = f"{
|
||||
start_coords.longitude},{
|
||||
start_coords.latitude};{
|
||||
end_coords.longitude},{
|
||||
end_coords.latitude}"
|
||||
coords_string = f"{start_coords.longitude},{start_coords.latitude};{
|
||||
end_coords.longitude
|
||||
},{end_coords.latitude}"
|
||||
url = f"{self.osrm_base_url}/{coords_string}"
|
||||
|
||||
params = {
|
||||
@@ -326,9 +319,9 @@ class RoadTripService:
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Route calculated: {
|
||||
route_info.formatted_distance}, {
|
||||
route_info.formatted_duration}"
|
||||
f"Route calculated: {route_info.formatted_distance}, {
|
||||
route_info.formatted_duration
|
||||
}"
|
||||
)
|
||||
return route_info
|
||||
else:
|
||||
@@ -350,11 +343,13 @@ class RoadTripService:
|
||||
Calculate straight-line distance as fallback when routing fails.
|
||||
"""
|
||||
# Haversine formula for great-circle distance
|
||||
lat1, lon1 = math.radians(start_coords.latitude), math.radians(
|
||||
start_coords.longitude
|
||||
lat1, lon1 = (
|
||||
math.radians(start_coords.latitude),
|
||||
math.radians(start_coords.longitude),
|
||||
)
|
||||
lat2, lon2 = math.radians(end_coords.latitude), math.radians(
|
||||
end_coords.longitude
|
||||
lat2, lon2 = (
|
||||
math.radians(end_coords.latitude),
|
||||
math.radians(end_coords.longitude),
|
||||
)
|
||||
|
||||
dlat = lat2 - lat1
|
||||
@@ -696,10 +691,7 @@ class RoadTripService:
|
||||
location.set_coordinates(coords.latitude, coords.longitude)
|
||||
location.save()
|
||||
logger.info(
|
||||
f"Geocoded park '{
|
||||
park.name}' to {
|
||||
coords.latitude}, {
|
||||
coords.longitude}"
|
||||
f"Geocoded park '{park.name}' to {coords.latitude}, {coords.longitude}"
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
Reference in New Issue
Block a user