""" Media API views for ThrillWiki API v1. This module provides API endpoints for media management including photo uploads, captions, and media operations. Consolidated from apps.media.views """ import json import logging from typing import Any, Dict from django.contrib.contenttypes.models import ContentType from django.core.exceptions import PermissionDenied from django.http import Http404 from drf_spectacular.utils import extend_schema, extend_schema_view, OpenApiParameter from drf_spectacular.types import OpenApiTypes from rest_framework import status from rest_framework.decorators import action from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.request import Request from rest_framework.response import Response from rest_framework.views import APIView from rest_framework.viewsets import ModelViewSet from rest_framework.parsers import MultiPartParser, FormParser # Import domain-specific models and services instead of generic Photo model from apps.parks.models import ParkPhoto from apps.rides.models import RidePhoto from apps.parks.services import ParkMediaService from apps.rides.services import RideMediaService from apps.core.services.media_service import MediaService from .serializers import ( PhotoUploadInputSerializer, PhotoUploadOutputSerializer, PhotoDetailOutputSerializer, PhotoUpdateInputSerializer, PhotoListOutputSerializer, ) from ..parks.serializers import ParkPhotoSerializer from ..rides.serializers import RidePhotoSerializer logger = logging.getLogger(__name__) @extend_schema_view( post=extend_schema( summary="Upload photo", description="Upload a photo and associate it with a content object (park, ride, etc.)", request=PhotoUploadInputSerializer, responses={ 201: PhotoUploadOutputSerializer, 400: OpenApiTypes.OBJECT, 403: OpenApiTypes.OBJECT, }, tags=["Media"], ), ) class PhotoUploadAPIView(APIView): """API endpoint for photo uploads.""" permission_classes = [IsAuthenticated] parser_classes = [MultiPartParser, FormParser] def post(self, request: Request) -> Response: """Upload a photo and associate it with a content object.""" try: serializer = PhotoUploadInputSerializer(data=request.data) if not serializer.is_valid(): return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) validated_data = serializer.validated_data # Get content object try: content_type = ContentType.objects.get( app_label=validated_data["app_label"], model=validated_data["model"] ) content_object = content_type.get_object_for_this_type( pk=validated_data["object_id"] ) except ContentType.DoesNotExist: return Response( { "error": f"Invalid content type: {validated_data['app_label']}.{validated_data['model']}" }, status=status.HTTP_400_BAD_REQUEST, ) except content_type.model_class().DoesNotExist: return Response( {"error": "Content object not found"}, status=status.HTTP_404_NOT_FOUND, ) # Determine which domain service to use based on content object if hasattr(content_object, '_meta') and content_object._meta.app_label == 'parks': # Check permissions for park photos if not request.user.has_perm("parks.add_parkphoto"): return Response( {"error": "You do not have permission to upload park photos"}, status=status.HTTP_403_FORBIDDEN, ) # Create park photo using park media service photo = ParkMediaService.upload_photo( park=content_object, image_file=validated_data["photo"], user=request.user, caption=validated_data.get("caption", ""), alt_text=validated_data.get("alt_text", ""), is_primary=validated_data.get("is_primary", False), ) elif hasattr(content_object, '_meta') and content_object._meta.app_label == 'rides': # Check permissions for ride photos if not request.user.has_perm("rides.add_ridephoto"): return Response( {"error": "You do not have permission to upload ride photos"}, status=status.HTTP_403_FORBIDDEN, ) # Create ride photo using ride media service photo = RideMediaService.upload_photo( ride=content_object, image_file=validated_data["photo"], user=request.user, caption=validated_data.get("caption", ""), alt_text=validated_data.get("alt_text", ""), is_primary=validated_data.get("is_primary", False), photo_type=validated_data.get("photo_type", "general"), ) else: return Response( {"error": f"Unsupported content type for media upload: {content_object._meta.label}"}, status=status.HTTP_400_BAD_REQUEST, ) response_serializer = PhotoUploadOutputSerializer( { "id": photo.id, "url": photo.image.url, "caption": photo.caption, "alt_text": photo.alt_text, "is_primary": photo.is_primary, "message": "Photo uploaded successfully", } ) return Response(response_serializer.data, status=status.HTTP_201_CREATED) except Exception as e: logger.error(f"Error in photo upload: {str(e)}", exc_info=True) return Response( {"error": f"An error occurred while uploading the photo: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR, ) @extend_schema_view( list=extend_schema( summary="List photos", description="Retrieve a list of photos with optional filtering", parameters=[ OpenApiParameter( name="content_type", type=OpenApiTypes.STR, location=OpenApiParameter.QUERY, description="Filter by content type (e.g., 'parks.park')", ), OpenApiParameter( name="object_id", type=OpenApiTypes.INT, location=OpenApiParameter.QUERY, description="Filter by object ID", ), ], responses={200: PhotoListOutputSerializer(many=True)}, tags=["Media"], ), retrieve=extend_schema( summary="Get photo details", description="Retrieve detailed information about a specific photo", responses={ 200: PhotoDetailOutputSerializer, 404: OpenApiTypes.OBJECT, }, tags=["Media"], ), update=extend_schema( summary="Update photo", description="Update photo information (caption, alt text, etc.)", request=PhotoUpdateInputSerializer, responses={ 200: PhotoDetailOutputSerializer, 400: OpenApiTypes.OBJECT, 403: OpenApiTypes.OBJECT, 404: OpenApiTypes.OBJECT, }, tags=["Media"], ), destroy=extend_schema( summary="Delete photo", description="Delete a photo (only by owner or admin)", responses={ 204: None, 403: OpenApiTypes.OBJECT, 404: OpenApiTypes.OBJECT, }, tags=["Media"], ), set_primary=extend_schema( summary="Set photo as primary", description="Set this photo as the primary photo for its content object", responses={ 200: OpenApiTypes.OBJECT, 403: OpenApiTypes.OBJECT, 404: OpenApiTypes.OBJECT, }, tags=["Media"], ), ) class PhotoViewSet(ModelViewSet): """ViewSet for managing photos.""" permission_classes = [IsAuthenticated] lookup_field = "id" def get_serializer_class(self): """Return appropriate serializer based on action.""" if self.action == "list": return PhotoListOutputSerializer elif self.action in ["update", "partial_update"]: return PhotoUpdateInputSerializer return PhotoDetailOutputSerializer