Refactor API structure and add comprehensive user management features

- Restructure API v1 with improved serializers organization
- Add user deletion requests and moderation queue system
- Implement bulk moderation operations and permissions
- Add user profile enhancements with display names and avatars
- Expand ride and park API endpoints with better filtering
- Add manufacturer API with detailed ride relationships
- Improve authentication flows and error handling
- Update frontend documentation and API specifications
This commit is contained in:
pacnpal
2025-08-29 16:03:51 -04:00
parent 7b9f64be72
commit bb7da85516
92 changed files with 19690 additions and 9076 deletions

View File

@@ -64,7 +64,7 @@ class MapLocationSerializer(serializers.Serializer):
@extend_schema_field(serializers.DictField())
def get_location(self, obj) -> dict:
"""Get location information."""
if hasattr(obj, 'location') and obj.location:
if hasattr(obj, "location") and obj.location:
return {
"city": obj.location.city,
"state": obj.location.state,
@@ -76,16 +76,20 @@ class MapLocationSerializer(serializers.Serializer):
@extend_schema_field(serializers.DictField())
def get_stats(self, obj) -> dict:
"""Get relevant statistics based on object type."""
if obj._meta.model_name == 'park':
if obj._meta.model_name == "park":
return {
"coaster_count": obj.coaster_count or 0,
"ride_count": obj.ride_count or 0,
"average_rating": float(obj.average_rating) if obj.average_rating else None,
"average_rating": (
float(obj.average_rating) if obj.average_rating else None
),
}
elif obj._meta.model_name == 'ride':
elif obj._meta.model_name == "ride":
return {
"category": obj.get_category_display() if obj.category else None,
"average_rating": float(obj.average_rating) if obj.average_rating else None,
"average_rating": (
float(obj.average_rating) if obj.average_rating else None
),
"park_name": obj.park.name if obj.park else None,
}
return {}
@@ -210,7 +214,7 @@ class MapSearchResultSerializer(serializers.Serializer):
@extend_schema_field(serializers.DictField())
def get_location(self, obj) -> dict:
"""Get location information."""
if hasattr(obj, 'location') and obj.location:
if hasattr(obj, "location") and obj.location:
return {
"city": obj.location.city,
"state": obj.location.state,
@@ -318,7 +322,7 @@ class MapLocationDetailSerializer(serializers.Serializer):
@extend_schema_field(serializers.DictField())
def get_location(self, obj) -> dict:
"""Get detailed location information."""
if hasattr(obj, 'location') and obj.location:
if hasattr(obj, "location") and obj.location:
return {
"street_address": obj.location.street_address,
"city": obj.location.city,
@@ -332,20 +336,28 @@ class MapLocationDetailSerializer(serializers.Serializer):
@extend_schema_field(serializers.DictField())
def get_stats(self, obj) -> dict:
"""Get detailed statistics based on object type."""
if obj._meta.model_name == 'park':
if obj._meta.model_name == "park":
return {
"coaster_count": obj.coaster_count or 0,
"ride_count": obj.ride_count or 0,
"average_rating": float(obj.average_rating) if obj.average_rating else None,
"average_rating": (
float(obj.average_rating) if obj.average_rating else None
),
"size_acres": float(obj.size_acres) if obj.size_acres else None,
"opening_date": obj.opening_date.isoformat() if obj.opening_date else None,
"opening_date": (
obj.opening_date.isoformat() if obj.opening_date else None
),
}
elif obj._meta.model_name == 'ride':
elif obj._meta.model_name == "ride":
return {
"category": obj.get_category_display() if obj.category else None,
"average_rating": float(obj.average_rating) if obj.average_rating else None,
"average_rating": (
float(obj.average_rating) if obj.average_rating else None
),
"park_name": obj.park.name if obj.park else None,
"opening_date": obj.opening_date.isoformat() if obj.opening_date else None,
"opening_date": (
obj.opening_date.isoformat() if obj.opening_date else None
),
"manufacturer": obj.manufacturer.name if obj.manufacturer else None,
}
return {}
@@ -370,13 +382,14 @@ class MapBoundsInputSerializer(serializers.Serializer):
def validate(self, attrs):
"""Validate that bounds make geographic sense."""
if attrs['north'] <= attrs['south']:
if attrs["north"] <= attrs["south"]:
raise serializers.ValidationError(
"North bound must be greater than south bound")
"North bound must be greater than south bound"
)
# Handle longitude wraparound (e.g., crossing the international date line)
# For now, we'll require west < east for simplicity
if attrs['west'] >= attrs['east']:
if attrs["west"] >= attrs["east"]:
raise serializers.ValidationError("West bound must be less than east bound")
return attrs
@@ -396,8 +409,8 @@ class MapSearchInputSerializer(serializers.Serializer):
if not value:
return []
valid_types = ['park', 'ride']
types = [t.strip().lower() for t in value.split(',')]
valid_types = ["park", "ride"]
types = [t.strip().lower() for t in value.split(",")]
for location_type in types:
if location_type not in valid_types: