from rest_framework import serializers from rest_framework_gis.fields import GeometryField from rest_framework_gis.serializers import GeoFeatureModelSerializer from apps.utils.storage import preview_url from .models import Challenge, ChallengeParticipant class ChallengeCreateSerializer(serializers.ModelSerializer): # Accepts GeoJSON Polygon from the client region = GeometryField(precision=6) class Meta: model = Challenge fields = ["title", "description", "region", "max_submissions", "expires_at"] def validate_region(self, value): if value.geom_type != "Polygon": raise serializers.ValidationError("region must be a GeoJSON Polygon.") return value class ChallengeUpdateSerializer(serializers.ModelSerializer): class Meta: model = Challenge fields = ["title", "description", "expires_at", "status"] class ChallengeMapSerializer(GeoFeatureModelSerializer): """ GeoJSON FeatureCollection using region_centroid as geometry. Used for the map pin list — does not include the full region polygon. """ coaster_count = serializers.SerializerMethodField() class Meta: model = Challenge geo_field = "region_centroid" fields = [ "id", "title", "status", "submission_count", "max_submissions", "coaster_count", "expires_at", "created_at", ] def get_coaster_count(self, obj): return obj.coasters.count() class ChallengeSplatPreviewSerializer(serializers.Serializer): id = serializers.UUIDField() preview_url = serializers.SerializerMethodField() created_at = serializers.DateTimeField() def get_preview_url(self, obj): return preview_url(obj.preview_key) class ChallengeDetailSerializer(serializers.ModelSerializer): region = serializers.SerializerMethodField() region_centroid = serializers.SerializerMethodField() creator_username = serializers.CharField(source="creator.username", read_only=True) participant_count = serializers.SerializerMethodField() is_participating = serializers.SerializerMethodField() preview_splats = serializers.SerializerMethodField() coaster_count = serializers.SerializerMethodField() class Meta: model = Challenge fields = [ "id", "title", "description", "status", "creator_username", "region", "region_centroid", "max_submissions", "submission_count", "participant_count", "is_participating", "coaster_count", "preview_splats", "expires_at", "created_at", "updated_at", ] def get_region(self, obj): import json return json.loads(obj.region.geojson) if obj.region else None def get_region_centroid(self, obj): import json return json.loads(obj.region_centroid.geojson) if obj.region_centroid else None def get_participant_count(self, obj): return obj.participants.count() def get_is_participating(self, obj): request = self.context.get("request") if not request or not request.user.is_authenticated: return False return obj.participants.filter(user=request.user).exists() def get_coaster_count(self, obj): return obj.coasters.count() def get_preview_splats(self, obj): from apps.splats.models import Splat qs = ( Splat.objects.filter(challenge=obj, is_published=True) .order_by("-created_at")[:5] ) return ChallengeSplatPreviewSerializer(qs, many=True).data class ChallengeParticipantSerializer(serializers.ModelSerializer): class Meta: model = ChallengeParticipant fields = ["id", "joined_at"] read_only_fields = fields