""" API serializers for the ride ranking system. """ from rest_framework import serializers from drf_spectacular.utils import ( extend_schema_serializer, extend_schema_field, OpenApiExample, ) from apps.rides.models import RideRanking, RankingSnapshot @extend_schema_serializer( examples=[ OpenApiExample( "Ride Ranking Example", summary="Example ranking response", description="A ride ranking with all metrics", value={ "id": 1, "rank": 1, "ride": { "id": 123, "name": "Steel Vengeance", "slug": "steel-vengeance", "park": {"id": 45, "name": "Cedar Point", "slug": "cedar-point"}, "category": "RC", }, "wins": 523, "losses": 87, "ties": 45, "winning_percentage": 0.8234, "mutual_riders_count": 1250, "comparison_count": 655, "average_rating": 9.2, "last_calculated": "2024-01-15T02:00:00Z", "rank_change": 2, "previous_rank": 3, }, ) ] ) class RideRankingSerializer(serializers.ModelSerializer): """Serializer for ride rankings.""" ride = serializers.SerializerMethodField() rank_change = serializers.SerializerMethodField() previous_rank = serializers.SerializerMethodField() class Meta: model = RideRanking fields = [ "id", "rank", "ride", "wins", "losses", "ties", "winning_percentage", "mutual_riders_count", "comparison_count", "average_rating", "last_calculated", "rank_change", "previous_rank", ] @extend_schema_field(serializers.DictField()) def get_ride(self, obj): """Get ride details.""" return { "id": obj.ride.id, "name": obj.ride.name, "slug": obj.ride.slug, "park": { "id": obj.ride.park.id, "name": obj.ride.park.name, "slug": obj.ride.park.slug, }, "category": obj.ride.category, } @extend_schema_field(serializers.IntegerField(allow_null=True)) def get_rank_change(self, obj): """Calculate rank change from previous snapshot.""" from apps.rides.models import RankingSnapshot latest_snapshots = RankingSnapshot.objects.filter(ride=obj.ride).order_by( "-snapshot_date" )[:2] if len(latest_snapshots) >= 2: return latest_snapshots[0].rank - latest_snapshots[1].rank return None @extend_schema_field(serializers.IntegerField(allow_null=True)) def get_previous_rank(self, obj): """Get previous rank.""" from apps.rides.models import RankingSnapshot latest_snapshots = RankingSnapshot.objects.filter(ride=obj.ride).order_by( "-snapshot_date" )[:2] if len(latest_snapshots) >= 2: return latest_snapshots[1].rank return None class RideRankingDetailSerializer(serializers.ModelSerializer): """Detailed serializer for a specific ride's ranking.""" ride = serializers.SerializerMethodField() head_to_head_comparisons = serializers.SerializerMethodField() ranking_history = serializers.SerializerMethodField() class Meta: model = RideRanking fields = [ "id", "rank", "ride", "wins", "losses", "ties", "winning_percentage", "mutual_riders_count", "comparison_count", "average_rating", "last_calculated", "calculation_version", "head_to_head_comparisons", "ranking_history", ] @extend_schema_field(serializers.DictField()) def get_ride(self, obj): """Get detailed ride information.""" ride = obj.ride return { "id": ride.id, "name": ride.name, "slug": ride.slug, "description": ride.description, "park": { "id": ride.park.id, "name": ride.park.name, "slug": ride.park.slug, "location": { "city": ( ride.park.location.city if hasattr(ride.park, "location") else None ), "state": ( ride.park.location.state if hasattr(ride.park, "location") else None ), "country": ( ride.park.location.country if hasattr(ride.park, "location") else None ), }, }, "category": ride.category, "manufacturer": ( {"id": ride.manufacturer.id, "name": ride.manufacturer.name} if ride.manufacturer else None ), "opening_date": ride.opening_date, "status": ride.status, } @extend_schema_field(serializers.ListField(child=serializers.DictField())) def get_head_to_head_comparisons(self, obj): """Get top head-to-head comparisons.""" from django.db.models import Q from apps.rides.models import RidePairComparison comparisons = ( RidePairComparison.objects.filter(Q(ride_a=obj.ride) | Q(ride_b=obj.ride)) .select_related("ride_a", "ride_b") .order_by("-mutual_riders_count")[:10] ) results = [] for comp in comparisons: if comp.ride_a == obj.ride: opponent = comp.ride_b wins = comp.ride_a_wins losses = comp.ride_b_wins else: opponent = comp.ride_a wins = comp.ride_b_wins losses = comp.ride_a_wins result = "win" if wins > losses else "loss" if losses > wins else "tie" results.append( { "opponent": { "id": opponent.id, "name": opponent.name, "slug": opponent.slug, "park": opponent.park.name, }, "wins": wins, "losses": losses, "ties": comp.ties, "result": result, "mutual_riders": comp.mutual_riders_count, } ) return results @extend_schema_field(serializers.ListField(child=serializers.DictField())) def get_ranking_history(self, obj): """Get recent ranking history.""" from apps.rides.models import RankingSnapshot history = RankingSnapshot.objects.filter(ride=obj.ride).order_by( "-snapshot_date" )[:30] return [ { "date": snapshot.snapshot_date, "rank": snapshot.rank, "winning_percentage": float(snapshot.winning_percentage), } for snapshot in history ] class RankingSnapshotSerializer(serializers.ModelSerializer): """Serializer for ranking history snapshots.""" ride_name = serializers.CharField(source="ride.name", read_only=True) park_name = serializers.CharField(source="ride.park.name", read_only=True) class Meta: model = RankingSnapshot fields = [ "id", "ride", "ride_name", "park_name", "rank", "winning_percentage", "snapshot_date", ] class RankingStatsSerializer(serializers.Serializer): """Serializer for ranking system statistics.""" total_ranked_rides = serializers.IntegerField() total_comparisons = serializers.IntegerField() last_calculation_time = serializers.DateTimeField() calculation_duration = serializers.FloatField() top_rated_ride = serializers.DictField() most_compared_ride = serializers.DictField() biggest_rank_change = serializers.DictField()