""" Park media serializers for ThrillWiki API v1. This module contains serializers for park-specific media functionality. Enhanced from rogue implementation to maintain full feature parity. """ from rest_framework import serializers from drf_spectacular.utils import ( extend_schema_field, extend_schema_serializer, OpenApiExample, ) from apps.parks.models import Park, ParkPhoto @extend_schema_serializer( examples=[ OpenApiExample( name="Park Photo with Cloudflare Images", summary="Complete park photo response", description="Example response showing all fields including Cloudflare Images URLs and variants", value={ "id": 456, "image": "https://imagedelivery.net/account-hash/def456ghi789/public", "image_url": "https://imagedelivery.net/account-hash/def456ghi789/public", "image_variants": { "thumbnail": "https://imagedelivery.net/account-hash/def456ghi789/thumbnail", "medium": "https://imagedelivery.net/account-hash/def456ghi789/medium", "large": "https://imagedelivery.net/account-hash/def456ghi789/large", "public": "https://imagedelivery.net/account-hash/def456ghi789/public", }, "caption": "Beautiful park entrance", "alt_text": "Main entrance gate with decorative archway", "is_primary": True, "is_approved": True, "created_at": "2023-01-01T12:00:00Z", "updated_at": "2023-01-01T12:00:00Z", "date_taken": "2023-01-01T11:00:00Z", "uploaded_by_username": "parkfan456", "file_size": 1536000, "dimensions": [1600, 900], "park_slug": "cedar-point", "park_name": "Cedar Point", }, ) ] ) class ParkPhotoOutputSerializer(serializers.ModelSerializer): """Enhanced output serializer for park photos with Cloudflare Images support.""" uploaded_by_username = serializers.CharField( source="uploaded_by.username", read_only=True ) file_size = serializers.SerializerMethodField() dimensions = serializers.SerializerMethodField() image_url = serializers.SerializerMethodField() image_variants = serializers.SerializerMethodField() @extend_schema_field( serializers.IntegerField(allow_null=True, help_text="File size in bytes") ) def get_file_size(self, obj): """Get file size in bytes.""" return obj.file_size @extend_schema_field( serializers.ListField( child=serializers.IntegerField(), min_length=2, max_length=2, allow_null=True, help_text="Image dimensions as [width, height] in pixels", ) ) def get_dimensions(self, obj): """Get image dimensions as [width, height].""" return obj.dimensions @extend_schema_field( serializers.URLField( help_text="Full URL to the Cloudflare Images asset", allow_null=True ) ) def get_image_url(self, obj): """Get the full Cloudflare Images URL.""" if obj.image: return obj.image.url return None @extend_schema_field( serializers.DictField( child=serializers.URLField(), help_text="Available Cloudflare Images variants with their URLs", ) ) def get_image_variants(self, obj): """Get available image variants from Cloudflare Images.""" if not obj.image: return {} # Common variants for park photos variants = { "thumbnail": f"{obj.image.url}/thumbnail", "medium": f"{obj.image.url}/medium", "large": f"{obj.image.url}/large", "public": f"{obj.image.url}/public", } return variants park_slug = serializers.CharField(source="park.slug", read_only=True) park_name = serializers.CharField(source="park.name", read_only=True) class Meta: model = ParkPhoto fields = [ "id", "image", "image_url", "image_variants", "caption", "alt_text", "is_primary", "is_approved", "created_at", "updated_at", "date_taken", "uploaded_by_username", "file_size", "dimensions", "park_slug", "park_name", ] read_only_fields = [ "id", "image_url", "image_variants", "created_at", "updated_at", "uploaded_by_username", "file_size", "dimensions", "park_slug", "park_name", ] class ParkPhotoCreateInputSerializer(serializers.ModelSerializer): """Input serializer for creating park photos.""" class Meta: model = ParkPhoto fields = [ "image", "caption", "alt_text", "is_primary", ] class ParkPhotoUpdateInputSerializer(serializers.ModelSerializer): """Input serializer for updating park photos.""" class Meta: model = ParkPhoto fields = [ "caption", "alt_text", "is_primary", ] class ParkPhotoListOutputSerializer(serializers.ModelSerializer): """Optimized output serializer for park photo lists.""" uploaded_by_username = serializers.CharField( source="uploaded_by.username", read_only=True ) class Meta: model = ParkPhoto fields = [ "id", "image", "caption", "is_primary", "is_approved", "created_at", "uploaded_by_username", ] read_only_fields = fields class ParkPhotoApprovalInputSerializer(serializers.Serializer): """Input serializer for bulk photo approval operations.""" photo_ids = serializers.ListField( child=serializers.IntegerField(), help_text="List of photo IDs to approve" ) approve = serializers.BooleanField( default=True, help_text="Whether to approve (True) or reject (False) the photos" ) class ParkPhotoStatsOutputSerializer(serializers.Serializer): """Output serializer for park photo statistics.""" total_photos = serializers.IntegerField() approved_photos = serializers.IntegerField() pending_photos = serializers.IntegerField() has_primary = serializers.BooleanField() recent_uploads = serializers.IntegerField() # Legacy serializers for backwards compatibility class ParkPhotoSerializer(serializers.ModelSerializer): """Legacy serializer for the ParkPhoto model - maintained for compatibility.""" class Meta: model = ParkPhoto fields = ( "id", "image", "caption", "alt_text", "is_primary", "uploaded_at", "uploaded_by", ) class HybridParkSerializer(serializers.ModelSerializer): """ Enhanced serializer for hybrid filtering strategy. Includes all filterable fields for client-side filtering. """ # Location fields from related ParkLocation city = serializers.SerializerMethodField() state = serializers.SerializerMethodField() country = serializers.SerializerMethodField() continent = serializers.SerializerMethodField() latitude = serializers.SerializerMethodField() longitude = serializers.SerializerMethodField() # Company fields operator_name = serializers.CharField(source="operator.name", read_only=True) property_owner_name = serializers.CharField(source="property_owner.name", read_only=True, allow_null=True) # Image URLs for display banner_image_url = serializers.SerializerMethodField() card_image_url = serializers.SerializerMethodField() # Computed fields for filtering opening_year = serializers.IntegerField(read_only=True) search_text = serializers.CharField(read_only=True) @extend_schema_field(serializers.CharField(allow_null=True)) def get_city(self, obj): """Get city from related location.""" try: return obj.location.city if hasattr(obj, 'location') and obj.location else None except AttributeError: return None @extend_schema_field(serializers.CharField(allow_null=True)) def get_state(self, obj): """Get state from related location.""" try: return obj.location.state if hasattr(obj, 'location') and obj.location else None except AttributeError: return None @extend_schema_field(serializers.CharField(allow_null=True)) def get_country(self, obj): """Get country from related location.""" try: return obj.location.country if hasattr(obj, 'location') and obj.location else None except AttributeError: return None @extend_schema_field(serializers.CharField(allow_null=True)) def get_continent(self, obj): """Get continent from related location.""" try: return obj.location.continent if hasattr(obj, 'location') and obj.location else None except AttributeError: return None @extend_schema_field(serializers.FloatField(allow_null=True)) def get_latitude(self, obj): """Get latitude from related location.""" try: if hasattr(obj, 'location') and obj.location and obj.location.coordinates: return obj.location.coordinates[1] # PostGIS returns [lon, lat] return None except (AttributeError, IndexError, TypeError): return None @extend_schema_field(serializers.FloatField(allow_null=True)) def get_longitude(self, obj): """Get longitude from related location.""" try: if hasattr(obj, 'location') and obj.location and obj.location.coordinates: return obj.location.coordinates[0] # PostGIS returns [lon, lat] return None except (AttributeError, IndexError, TypeError): return None @extend_schema_field(serializers.URLField(allow_null=True)) def get_banner_image_url(self, obj): """Get banner image URL.""" if obj.banner_image and obj.banner_image.image: return obj.banner_image.image.url return None @extend_schema_field(serializers.URLField(allow_null=True)) def get_card_image_url(self, obj): """Get card image URL.""" if obj.card_image and obj.card_image.image: return obj.card_image.image.url return None class Meta: model = Park fields = [ # Basic park info "id", "name", "slug", "description", "status", "park_type", # Dates and computed fields "opening_date", "closing_date", "opening_year", "operating_season", # Location fields "city", "state", "country", "continent", "latitude", "longitude", # Company relationships "operator_name", "property_owner_name", # Statistics "size_acres", "average_rating", "ride_count", "coaster_count", # Images "banner_image_url", "card_image_url", # URLs "website", "url", # Computed fields for filtering "search_text", # Metadata "created_at", "updated_at", ] read_only_fields = fields class ParkSerializer(serializers.ModelSerializer): """Serializer for the Park model.""" class Meta: model = Park fields = ( "id", "name", "slug", "country", "continent", "latitude", "longitude", "website", "status", )