from django.conf import settings from django.utils import timezone from rest_framework import serializers from rest_framework_gis.serializers import GeoFeatureModelSerializer from apps.utils.storage import generate_presigned_put_url, preview_url from .models import Splat class SplatJobSummarySerializer(serializers.Serializer): """Inlined job status — used in SplatMineSerializer.""" id = serializers.UUIDField() status = serializers.CharField() current_step = serializers.CharField() progress = serializers.IntegerField() error_message = serializers.CharField() queued_at = serializers.DateTimeField() started_at = serializers.DateTimeField() finished_at = serializers.DateTimeField() class SplatCreateSerializer(serializers.ModelSerializer): challenge_id = serializers.UUIDField(required=False, allow_null=True) capture_metadata = serializers.JSONField() class Meta: model = Splat fields = ["challenge_id", "capture_metadata"] def validate_capture_metadata(self, value): frames = value.get("frames", []) min_frames = settings.MIN_CAPTURE_FRAMES if len(frames) < min_frames: raise serializers.ValidationError( f"capture_metadata must contain at least {min_frames} frames; " f"got {len(frames)}." ) return value class SplatCreateResponseSerializer(serializers.Serializer): """Shape of the POST /splats/ response — not a ModelSerializer.""" id = serializers.UUIDField() status = serializers.CharField() upload_url = serializers.CharField(allow_null=True) upload_expires_at = serializers.DateTimeField() class SplatMapSerializer(GeoFeatureModelSerializer): """GeoJSON FeatureCollection for the map tile endpoint.""" preview_url = serializers.SerializerMethodField() class Meta: model = Splat geo_field = "location" fields = ["id", "heading", "altitude", "preview_url", "splat_file_size", "created_at"] def get_preview_url(self, obj): return preview_url(obj.preview_key) class SplatDetailSerializer(serializers.ModelSerializer): coverage = serializers.SerializerMethodField() preview_url = serializers.SerializerMethodField() owner_username = serializers.CharField(source="owner.username", read_only=True) challenge_id = serializers.UUIDField(read_only=True) class Meta: model = Splat fields = [ "id", "owner_username", "challenge_id", "status", "is_published", "location", "coverage", "heading", "altitude", "preview_url", "splat_file_size", "quality_score", "frame_count", "created_at", "updated_at", ] def get_coverage(self, obj): if obj.coverage: return obj.coverage.geojson return None def get_preview_url(self, obj): return preview_url(obj.preview_key) class SplatMineSerializer(serializers.ModelSerializer): job = serializers.SerializerMethodField() preview_url = serializers.SerializerMethodField() class Meta: model = Splat fields = [ "id", "status", "is_published", "challenge_id", "preview_url", "quality_score", "frame_count", "created_at", "updated_at", "job", ] def get_job(self, obj): try: j = obj.job except Splat.job.RelatedObjectDoesNotExist: return None return SplatJobSummarySerializer(j).data def get_preview_url(self, obj): return preview_url(obj.preview_key)