""" Serializers for Rides API following Django styleguide patterns. """ from rest_framework import serializers from ..models import Ride class RideModelOutputSerializer(serializers.Serializer): """Output serializer for ride model data.""" id = serializers.IntegerField() name = serializers.CharField() description = serializers.CharField() category = serializers.CharField() manufacturer = serializers.SerializerMethodField() def get_manufacturer(self, obj): if obj.manufacturer: return { "id": obj.manufacturer.id, "name": obj.manufacturer.name, "slug": obj.manufacturer.slug, } return None class RideParkOutputSerializer(serializers.Serializer): """Output serializer for ride's park data.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() class RideListOutputSerializer(serializers.Serializer): """Output serializer for ride list view.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() category = serializers.CharField() status = serializers.CharField() description = serializers.CharField() # Park info park = RideParkOutputSerializer() # Statistics average_rating = serializers.DecimalField( max_digits=3, decimal_places=2, allow_null=True ) capacity_per_hour = serializers.IntegerField(allow_null=True) # Dates opening_date = serializers.DateField(allow_null=True) closing_date = serializers.DateField(allow_null=True) # Metadata created_at = serializers.DateTimeField() updated_at = serializers.DateTimeField() class RideDetailOutputSerializer(serializers.Serializer): """Output serializer for ride detail view.""" id = serializers.IntegerField() name = serializers.CharField() slug = serializers.CharField() category = serializers.CharField() status = serializers.CharField() post_closing_status = serializers.CharField(allow_null=True) description = serializers.CharField() # Park info park = RideParkOutputSerializer() park_area = serializers.SerializerMethodField() # Dates opening_date = serializers.DateField(allow_null=True) closing_date = serializers.DateField(allow_null=True) status_since = serializers.DateField(allow_null=True) # Physical specs min_height_in = serializers.IntegerField(allow_null=True) max_height_in = serializers.IntegerField(allow_null=True) capacity_per_hour = serializers.IntegerField(allow_null=True) ride_duration_seconds = serializers.IntegerField(allow_null=True) # Statistics average_rating = serializers.DecimalField( max_digits=3, decimal_places=2, allow_null=True ) # Companies manufacturer = serializers.SerializerMethodField() designer = serializers.SerializerMethodField() # Model ride_model = RideModelOutputSerializer(allow_null=True) # Metadata created_at = serializers.DateTimeField() updated_at = serializers.DateTimeField() def get_park_area(self, obj): if obj.park_area: return { "id": obj.park_area.id, "name": obj.park_area.name, "slug": obj.park_area.slug, } return None def get_manufacturer(self, obj): if obj.manufacturer: return { "id": obj.manufacturer.id, "name": obj.manufacturer.name, "slug": obj.manufacturer.slug, } return None def get_designer(self, obj): if obj.designer: return { "id": obj.designer.id, "name": obj.designer.name, "slug": obj.designer.slug, } return None class RideCreateInputSerializer(serializers.Serializer): """Input serializer for creating rides.""" name = serializers.CharField(max_length=255) description = serializers.CharField(allow_blank=True, default="") category = serializers.ChoiceField(choices=Ride.CATEGORY_CHOICES) status = serializers.ChoiceField(choices=Ride.STATUS_CHOICES, default="OPERATING") # Required park park_id = serializers.IntegerField() # Optional area park_area_id = serializers.IntegerField(required=False, allow_null=True) # Optional dates opening_date = serializers.DateField(required=False, allow_null=True) closing_date = serializers.DateField(required=False, allow_null=True) status_since = serializers.DateField(required=False, allow_null=True) # Optional specs min_height_in = serializers.IntegerField( required=False, allow_null=True, min_value=30, max_value=90 ) max_height_in = serializers.IntegerField( required=False, allow_null=True, min_value=30, max_value=90 ) capacity_per_hour = serializers.IntegerField( required=False, allow_null=True, min_value=1 ) ride_duration_seconds = serializers.IntegerField( required=False, allow_null=True, min_value=1 ) # Optional companies manufacturer_id = serializers.IntegerField(required=False, allow_null=True) designer_id = serializers.IntegerField(required=False, allow_null=True) # Optional model ride_model_id = serializers.IntegerField(required=False, allow_null=True) def validate(self, data): """Cross-field validation.""" # Date validation opening_date = data.get("opening_date") closing_date = data.get("closing_date") if opening_date and closing_date and closing_date < opening_date: raise serializers.ValidationError( "Closing date cannot be before opening date" ) # Height validation min_height = data.get("min_height_in") max_height = data.get("max_height_in") if min_height and max_height and min_height > max_height: raise serializers.ValidationError( "Minimum height cannot be greater than maximum height" ) return data class RideUpdateInputSerializer(serializers.Serializer): """Input serializer for updating rides.""" name = serializers.CharField(max_length=255, required=False) description = serializers.CharField(allow_blank=True, required=False) category = serializers.ChoiceField(choices=Ride.CATEGORY_CHOICES, required=False) status = serializers.ChoiceField(choices=Ride.STATUS_CHOICES, required=False) post_closing_status = serializers.ChoiceField( choices=Ride.POST_CLOSING_STATUS_CHOICES, required=False, allow_null=True, ) # Park and area park_id = serializers.IntegerField(required=False) park_area_id = serializers.IntegerField(required=False, allow_null=True) # Dates opening_date = serializers.DateField(required=False, allow_null=True) closing_date = serializers.DateField(required=False, allow_null=True) status_since = serializers.DateField(required=False, allow_null=True) # Specs min_height_in = serializers.IntegerField( required=False, allow_null=True, min_value=30, max_value=90 ) max_height_in = serializers.IntegerField( required=False, allow_null=True, min_value=30, max_value=90 ) capacity_per_hour = serializers.IntegerField( required=False, allow_null=True, min_value=1 ) ride_duration_seconds = serializers.IntegerField( required=False, allow_null=True, min_value=1 ) # Companies manufacturer_id = serializers.IntegerField(required=False, allow_null=True) designer_id = serializers.IntegerField(required=False, allow_null=True) # Model ride_model_id = serializers.IntegerField(required=False, allow_null=True) def validate(self, data): """Cross-field validation.""" # Date validation opening_date = data.get("opening_date") closing_date = data.get("closing_date") if opening_date and closing_date and closing_date < opening_date: raise serializers.ValidationError( "Closing date cannot be before opening date" ) # Height validation min_height = data.get("min_height_in") max_height = data.get("max_height_in") if min_height and max_height and min_height > max_height: raise serializers.ValidationError( "Minimum height cannot be greater than maximum height" ) return data class RideFilterInputSerializer(serializers.Serializer): """Input serializer for ride filtering and search.""" # Search search = serializers.CharField(required=False, allow_blank=True) # Category filter category = serializers.MultipleChoiceField( choices=Ride.CATEGORY_CHOICES, required=False ) # Status filter status = serializers.MultipleChoiceField( choices=Ride.STATUS_CHOICES, required=False ) # Park filter park_id = serializers.IntegerField(required=False) park_slug = serializers.CharField(required=False, allow_blank=True) # Company filters manufacturer_id = serializers.IntegerField(required=False) designer_id = serializers.IntegerField(required=False) # Rating filter min_rating = serializers.DecimalField( max_digits=3, decimal_places=2, required=False, min_value=1, max_value=10, ) # Height filters min_height_requirement = serializers.IntegerField(required=False) max_height_requirement = serializers.IntegerField(required=False) # Capacity filter min_capacity = serializers.IntegerField(required=False) # Ordering ordering = serializers.ChoiceField( choices=[ "name", "-name", "opening_date", "-opening_date", "average_rating", "-average_rating", "capacity_per_hour", "-capacity_per_hour", "created_at", "-created_at", ], required=False, default="name", ) class RideStatsOutputSerializer(serializers.Serializer): """Output serializer for ride statistics.""" total_rides = serializers.IntegerField() operating_rides = serializers.IntegerField() closed_rides = serializers.IntegerField() under_construction = serializers.IntegerField() # By category rides_by_category = serializers.DictField() # Averages average_rating = serializers.DecimalField( max_digits=3, decimal_places=2, allow_null=True ) average_capacity = serializers.DecimalField( max_digits=8, decimal_places=2, allow_null=True ) # Top manufacturers top_manufacturers = serializers.ListField(child=serializers.DictField()) # Recently added recently_added_count = serializers.IntegerField()