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:
pacnpal
2025-08-26 14:40:46 -04:00
parent 831be6a2ee
commit e4e36c7899
133 changed files with 1321 additions and 1001 deletions

View File

@@ -5,8 +5,6 @@ Handles location management for individual rides within parks.
import requests
from typing import List, Dict, Any, Optional, Tuple
from django.conf import settings
from django.core.cache import cache
from django.db import transaction
import logging
@@ -317,7 +315,6 @@ class RideLocationService:
ride_location.ride.name.lower() in display_name
and park.name.lower() in display_name
):
# Update the ride location
ride_location.set_coordinates(
float(result["lat"]), float(result["lon"])

View File

@@ -28,7 +28,7 @@ class RideMediaService:
alt_text: str = "",
photo_type: str = "exterior",
is_primary: bool = False,
auto_approve: bool = False
auto_approve: bool = False,
) -> RidePhoto:
"""
Upload a photo for a ride.
@@ -67,7 +67,7 @@ class RideMediaService:
photo_type=photo_type,
is_primary=is_primary,
is_approved=auto_approve,
uploaded_by=user
uploaded_by=user,
)
# Extract EXIF date
@@ -83,7 +83,7 @@ class RideMediaService:
ride: Ride,
approved_only: bool = True,
primary_first: bool = True,
photo_type: Optional[str] = None
photo_type: Optional[str] = None,
) -> List[RidePhoto]:
"""
Get photos for a ride.
@@ -106,9 +106,9 @@ class RideMediaService:
queryset = queryset.filter(photo_type=photo_type)
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)
@@ -141,10 +141,9 @@ class RideMediaService:
List of RidePhoto instances
"""
return list(
ride.photos.filter(
photo_type=photo_type,
is_approved=True
).order_by('-created_at')
ride.photos.filter(photo_type=photo_type, is_approved=True).order_by(
"-created_at"
)
)
@staticmethod
@@ -217,7 +216,8 @@ class RideMediaService:
photo.delete()
logger.info(
f"Photo {photo_id} deleted from ride {ride_slug} by user {deleted_by.username}")
f"Photo {photo_id} deleted from ride {ride_slug} by user {deleted_by.username}"
)
return True
except Exception as e:
logger.error(f"Failed to delete photo {photo.pk}: {str(e)}")
@@ -238,7 +238,7 @@ class RideMediaService:
# Get counts by photo type
type_counts = {}
for photo_type, _ in RidePhoto._meta.get_field('photo_type').choices:
for photo_type, _ in RidePhoto._meta.get_field("photo_type").choices:
type_counts[photo_type] = photos.filter(photo_type=photo_type).count()
return {
@@ -246,8 +246,8 @@ class RideMediaService:
"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(),
"by_type": type_counts
"recent_uploads": photos.order_by("-created_at")[:5].count(),
"by_type": type_counts,
}
@staticmethod
@@ -270,7 +270,8 @@ class RideMediaService:
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
@staticmethod
@@ -285,10 +286,9 @@ class RideMediaService:
List of construction RidePhoto instances ordered by date taken
"""
return list(
ride.photos.filter(
photo_type='construction',
is_approved=True
).order_by('date_taken', 'created_at')
ride.photos.filter(photo_type="construction", is_approved=True).order_by(
"date_taken", "created_at"
)
)
@staticmethod
@@ -302,4 +302,4 @@ class RideMediaService:
Returns:
List of on-ride RidePhoto instances
"""
return RideMediaService.get_photos_by_type(ride, 'onride')
return RideMediaService.get_photos_by_type(ride, "onride")

View File

@@ -12,7 +12,7 @@ from decimal import Decimal
from datetime import date
from django.db import transaction
from django.db.models import Avg, Count, Q, F
from django.db.models import Avg, Count, Q
from django.utils import timezone
from apps.rides.models import (