mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 07:11:08 -05:00
- Implemented PrimeProgress component with support for labels, helper text, and various styles (size, variant, color). - Created PrimeSelect component with dropdown functionality, custom templates, and validation states. - Developed PrimeSkeleton component for loading placeholders with different shapes and animations. - Updated index.ts to export new components for easy import. - Enhanced PrimeVueTest.vue to include tests for new components and their functionalities. - Introduced a custom ThrillWiki theme for PrimeVue with tailored color schemes and component styles. - Added ambient type declarations for various components to improve TypeScript support.
269 lines
8.3 KiB
Python
269 lines
8.3 KiB
Python
"""
|
|
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()
|