Files
thrillwiki_django_no_react/apps/api/v1/serializers/services.py
2025-09-21 20:19:12 -04:00

267 lines
9.2 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,
)
# === HEALTH CHECK SERIALIZERS ===
class HealthCheckOutputSerializer(serializers.Serializer):
"""Output serializer for comprehensive health check responses."""
status = serializers.CharField(help_text="Overall health status")
timestamp = serializers.DateTimeField(help_text="Timestamp of health check")
version = serializers.CharField(help_text="Application version")
environment = serializers.CharField(help_text="Environment name")
response_time_ms = serializers.FloatField(help_text="Response time in milliseconds")
checks = serializers.DictField(help_text="Individual health check results")
metrics = serializers.DictField(help_text="System metrics")
class PerformanceMetricsOutputSerializer(serializers.Serializer):
"""Output serializer for performance metrics responses."""
timestamp = serializers.DateTimeField(help_text="Timestamp of metrics collection")
database_analysis = serializers.DictField(help_text="Database performance analysis")
cache_performance = serializers.DictField(help_text="Cache performance metrics")
recent_slow_queries = serializers.DictField(help_text="Recent slow query analysis")
class SimpleHealthOutputSerializer(serializers.Serializer):
"""Output serializer for simple health check responses."""
status = serializers.CharField(help_text="Simple health status")
timestamp = serializers.DateTimeField(help_text="Timestamp of health check")
error = serializers.CharField(
required=False, help_text="Error message if unhealthy"
)
# === 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)
template_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", "Edit Submission"),
("PHOTO", "Photo Submission"),
("REVIEW", "Review Submission"),
],
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, attrs):
"""Validate that park IDs are different."""
if attrs["park1_id"] == attrs["park2_id"]:
raise serializers.ValidationError("Park IDs must be different")
return attrs
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)