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

@@ -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")