Files
thrillwiki_django_no_react/backend/apps/api/v1/serializers/services.py
pacnpal e4e36c7899 Add migrations for ParkPhoto and RidePhoto models with associated events
- Created ParkPhoto and ParkPhotoEvent models in the parks app, including fields for image, caption, alt text, and relationships to the Park model.
- Implemented triggers for insert and update operations on ParkPhoto to log changes in ParkPhotoEvent.
- Created RidePhoto and RidePhotoEvent models in the rides app, with similar structure and functionality as ParkPhoto.
- Added fields for photo type in RidePhoto and implemented corresponding triggers for logging changes.
- Established necessary indexes and unique constraints for both models to ensure data integrity and optimize queries.
2025-08-26 14:40:46 -04:00

228 lines
7.6 KiB
Python

"""
Services domain serializers for ThrillWiki API v1.
This module contains serializers for various services like email, maps,
history tracking, moderation, and roadtrip planning.
"""
from rest_framework import serializers
from drf_spectacular.utils import (
extend_schema_field,
)
# === EMAIL SERVICE SERIALIZERS ===
class EmailSendInputSerializer(serializers.Serializer):
"""Input serializer for sending emails."""
to = serializers.EmailField()
subject = serializers.CharField(max_length=255)
text = serializers.CharField()
html = serializers.CharField(required=False)
template = serializers.CharField(required=False)
context = serializers.JSONField(required=False)
class EmailTemplateOutputSerializer(serializers.Serializer):
"""Output serializer for email templates."""
id = serializers.CharField()
name = serializers.CharField()
subject = serializers.CharField()
text_template = serializers.CharField()
html_template = serializers.CharField(required=False)
# === MAP SERVICE SERIALIZERS ===
class MapDataOutputSerializer(serializers.Serializer):
"""Output serializer for map data."""
parks = serializers.ListField(child=serializers.DictField())
rides = serializers.ListField(child=serializers.DictField())
bounds = serializers.DictField()
zoom_level = serializers.IntegerField()
class CoordinateInputSerializer(serializers.Serializer):
"""Input serializer for coordinate-based requests."""
latitude = serializers.FloatField(min_value=-90, max_value=90)
longitude = serializers.FloatField(min_value=-180, max_value=180)
radius_km = serializers.FloatField(min_value=0, max_value=1000, default=10)
# === HISTORY SERIALIZERS ===
class HistoryEventSerializer(serializers.Serializer):
"""Base serializer for history events from pghistory."""
pgh_id = serializers.IntegerField(read_only=True)
pgh_created_at = serializers.DateTimeField(read_only=True)
pgh_label = serializers.CharField(read_only=True)
pgh_obj_id = serializers.IntegerField(read_only=True)
pgh_context = serializers.JSONField(read_only=True, allow_null=True)
pgh_diff = serializers.SerializerMethodField()
@extend_schema_field(serializers.DictField())
def get_pgh_diff(self, obj) -> dict:
"""Get diff from previous version if available."""
if hasattr(obj, "diff_against_previous"):
return obj.diff_against_previous()
return {}
class HistoryEntryOutputSerializer(serializers.Serializer):
"""Output serializer for history entries."""
id = serializers.IntegerField()
model_type = serializers.CharField()
object_id = serializers.IntegerField()
object_name = serializers.CharField()
action = serializers.CharField()
changes = serializers.JSONField()
timestamp = serializers.DateTimeField()
user = serializers.SerializerMethodField()
@extend_schema_field(serializers.DictField(allow_null=True))
def get_user(self, obj) -> dict | None:
if hasattr(obj, "user") and obj.user:
return {
"id": obj.user.id,
"username": obj.user.username,
}
return None
class HistoryCreateInputSerializer(serializers.Serializer):
"""Input serializer for creating history entries."""
action = serializers.CharField(max_length=50)
description = serializers.CharField(max_length=500)
metadata = serializers.JSONField(required=False)
# === MODERATION SERIALIZERS ===
class ModerationSubmissionSerializer(serializers.Serializer):
"""Serializer for moderation submissions."""
submission_type = serializers.ChoiceField(
choices=["EDIT", "PHOTO", "REVIEW"], help_text="Type of submission"
)
content_type = serializers.CharField(help_text="Content type being modified")
object_id = serializers.IntegerField(help_text="ID of object being modified")
changes = serializers.JSONField(help_text="Changes being submitted")
reason = serializers.CharField(
max_length=500,
required=False,
allow_blank=True,
help_text="Reason for the changes",
)
class ModerationSubmissionOutputSerializer(serializers.Serializer):
"""Output serializer for moderation submission responses."""
status = serializers.CharField()
message = serializers.CharField()
submission_id = serializers.IntegerField(required=False)
auto_approved = serializers.BooleanField(required=False)
# === ROADTRIP SERIALIZERS ===
class RoadtripParkSerializer(serializers.Serializer):
"""Serializer for parks in roadtrip planning."""
id = serializers.IntegerField()
name = serializers.CharField()
slug = serializers.CharField()
latitude = serializers.FloatField()
longitude = serializers.FloatField()
coaster_count = serializers.IntegerField()
status = serializers.CharField()
class RoadtripCreateInputSerializer(serializers.Serializer):
"""Input serializer for creating roadtrips."""
name = serializers.CharField(max_length=255)
park_ids = serializers.ListField(
child=serializers.IntegerField(),
min_length=2,
max_length=10,
help_text="List of park IDs (2-10 parks)",
)
start_date = serializers.DateField(required=False)
end_date = serializers.DateField(required=False)
notes = serializers.CharField(max_length=1000, required=False, allow_blank=True)
def validate_park_ids(self, value):
"""Validate park IDs."""
if len(value) < 2:
raise serializers.ValidationError("At least 2 parks are required")
if len(value) > 10:
raise serializers.ValidationError("Maximum 10 parks allowed")
if len(set(value)) != len(value):
raise serializers.ValidationError("Duplicate park IDs not allowed")
return value
class RoadtripOutputSerializer(serializers.Serializer):
"""Output serializer for roadtrip responses."""
id = serializers.CharField()
name = serializers.CharField()
parks = RoadtripParkSerializer(many=True)
total_distance_miles = serializers.FloatField()
estimated_drive_time_hours = serializers.FloatField()
route_coordinates = serializers.ListField(
child=serializers.ListField(child=serializers.FloatField())
)
created_at = serializers.DateTimeField()
class GeocodeInputSerializer(serializers.Serializer):
"""Input serializer for geocoding requests."""
address = serializers.CharField(max_length=500, help_text="Address to geocode")
class GeocodeOutputSerializer(serializers.Serializer):
"""Output serializer for geocoding responses."""
status = serializers.CharField()
coordinates = serializers.JSONField(required=False)
formatted_address = serializers.CharField(required=False)
# === DISTANCE CALCULATION SERIALIZERS ===
class DistanceCalculationInputSerializer(serializers.Serializer):
"""Input serializer for distance calculation requests."""
park1_id = serializers.IntegerField(help_text="ID of first park")
park2_id = serializers.IntegerField(help_text="ID of second park")
def validate(self, data):
"""Validate that park IDs are different."""
if data["park1_id"] == data["park2_id"]:
raise serializers.ValidationError("Park IDs must be different")
return data
class DistanceCalculationOutputSerializer(serializers.Serializer):
"""Output serializer for distance calculation responses."""
status = serializers.CharField()
distance_miles = serializers.FloatField(required=False)
distance_km = serializers.FloatField(required=False)
drive_time_hours = serializers.FloatField(required=False)
message = serializers.CharField(required=False)