mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 16:11:08 -05:00
Removed VueJS frontend and dramatically enhanced API
This commit is contained in:
@@ -146,9 +146,10 @@ def _import_accounts_symbols() -> Dict[str, Any]:
|
||||
|
||||
_accounts = _import_accounts_symbols()
|
||||
|
||||
# Bind account symbols into the module namespace (either actual objects or None)
|
||||
# Bind account symbols into the module namespace (only if they exist)
|
||||
for _name in _ACCOUNTS_SYMBOLS:
|
||||
globals()[_name] = _accounts.get(_name)
|
||||
if _accounts.get(_name) is not None:
|
||||
globals()[_name] = _accounts[_name]
|
||||
|
||||
# --- Services domain ---
|
||||
|
||||
@@ -255,22 +256,79 @@ _SERVICES_EXPORTS = [
|
||||
"DistanceCalculationOutputSerializer",
|
||||
]
|
||||
|
||||
# Build __all__ from known exports plus any serializer-like names discovered above
|
||||
__all__ = (
|
||||
_SHARED_EXPORTS
|
||||
+ _PARKS_EXPORTS
|
||||
+ _COMPANIES_EXPORTS
|
||||
+ _RIDES_EXPORTS
|
||||
+ _SERVICES_EXPORTS
|
||||
+ _ACCOUNTS_SYMBOLS
|
||||
)
|
||||
# Build a static __all__ list with only the serializers we know exist
|
||||
__all__ = [
|
||||
# Shared exports
|
||||
"CATEGORY_CHOICES",
|
||||
"ModelChoices",
|
||||
"LocationOutputSerializer",
|
||||
"CompanyOutputSerializer",
|
||||
"UserModel",
|
||||
|
||||
# Parks exports
|
||||
"ParkListOutputSerializer",
|
||||
"ParkDetailOutputSerializer",
|
||||
"ParkCreateInputSerializer",
|
||||
"ParkUpdateInputSerializer",
|
||||
"ParkFilterInputSerializer",
|
||||
"ParkAreaDetailOutputSerializer",
|
||||
"ParkAreaCreateInputSerializer",
|
||||
"ParkAreaUpdateInputSerializer",
|
||||
"ParkLocationOutputSerializer",
|
||||
"ParkLocationCreateInputSerializer",
|
||||
"ParkLocationUpdateInputSerializer",
|
||||
"ParkSuggestionSerializer",
|
||||
"ParkSuggestionOutputSerializer",
|
||||
|
||||
# Companies exports
|
||||
"CompanyDetailOutputSerializer",
|
||||
"CompanyCreateInputSerializer",
|
||||
"CompanyUpdateInputSerializer",
|
||||
"RideModelDetailOutputSerializer",
|
||||
"RideModelCreateInputSerializer",
|
||||
"RideModelUpdateInputSerializer",
|
||||
|
||||
# Rides exports
|
||||
"RideParkOutputSerializer",
|
||||
"RideModelOutputSerializer",
|
||||
"RideListOutputSerializer",
|
||||
"RideDetailOutputSerializer",
|
||||
"RideCreateInputSerializer",
|
||||
"RideUpdateInputSerializer",
|
||||
"RideFilterInputSerializer",
|
||||
"RollerCoasterStatsOutputSerializer",
|
||||
"RollerCoasterStatsCreateInputSerializer",
|
||||
"RollerCoasterStatsUpdateInputSerializer",
|
||||
"RideLocationOutputSerializer",
|
||||
"RideLocationCreateInputSerializer",
|
||||
"RideLocationUpdateInputSerializer",
|
||||
"RideReviewOutputSerializer",
|
||||
"RideReviewCreateInputSerializer",
|
||||
"RideReviewUpdateInputSerializer",
|
||||
|
||||
# Services exports
|
||||
"HealthCheckOutputSerializer",
|
||||
"PerformanceMetricsOutputSerializer",
|
||||
"SimpleHealthOutputSerializer",
|
||||
"EmailSendInputSerializer",
|
||||
"EmailTemplateOutputSerializer",
|
||||
"MapDataOutputSerializer",
|
||||
"CoordinateInputSerializer",
|
||||
"HistoryEventSerializer",
|
||||
"HistoryEntryOutputSerializer",
|
||||
"HistoryCreateInputSerializer",
|
||||
"ModerationSubmissionSerializer",
|
||||
"ModerationSubmissionOutputSerializer",
|
||||
"RoadtripParkSerializer",
|
||||
"RoadtripCreateInputSerializer",
|
||||
"RoadtripOutputSerializer",
|
||||
"GeocodeInputSerializer",
|
||||
"GeocodeOutputSerializer",
|
||||
"DistanceCalculationInputSerializer",
|
||||
"DistanceCalculationOutputSerializer",
|
||||
]
|
||||
|
||||
# Add any discovered globals that look like serializers (avoid duplicates)
|
||||
for name in list(globals().keys()):
|
||||
if name in __all__:
|
||||
continue
|
||||
if name.endswith(("Serializer", "OutputSerializer", "InputSerializer")):
|
||||
# Add any accounts serializers that actually exist
|
||||
for name in _ACCOUNTS_SYMBOLS:
|
||||
if name in globals():
|
||||
__all__.append(name)
|
||||
|
||||
# Ensure __all__ is a flat list of unique strings (preserve order)
|
||||
__all__ = list(dict.fromkeys(__all__))
|
||||
|
||||
408
backend/apps/api/v1/serializers/maps.py
Normal file
408
backend/apps/api/v1/serializers/maps.py
Normal file
@@ -0,0 +1,408 @@
|
||||
"""
|
||||
Maps domain serializers for ThrillWiki API v1.
|
||||
|
||||
This module contains all serializers related to map functionality,
|
||||
including location data, search results, and clustering.
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
from drf_spectacular.utils import (
|
||||
extend_schema_serializer,
|
||||
extend_schema_field,
|
||||
OpenApiExample,
|
||||
)
|
||||
|
||||
|
||||
# === MAP LOCATION SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Map Location Example",
|
||||
summary="Example map location response",
|
||||
description="A location point on the map",
|
||||
value={
|
||||
"id": 1,
|
||||
"type": "park",
|
||||
"name": "Cedar Point",
|
||||
"slug": "cedar-point",
|
||||
"latitude": 41.4793,
|
||||
"longitude": -82.6833,
|
||||
"status": "OPERATING",
|
||||
"location": {
|
||||
"city": "Sandusky",
|
||||
"state": "Ohio",
|
||||
"country": "United States",
|
||||
},
|
||||
"stats": {
|
||||
"coaster_count": 17,
|
||||
"ride_count": 70,
|
||||
"average_rating": 4.5,
|
||||
},
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class MapLocationSerializer(serializers.Serializer):
|
||||
"""Serializer for individual map locations (parks and rides)."""
|
||||
|
||||
id = serializers.IntegerField()
|
||||
type = serializers.CharField() # 'park' or 'ride'
|
||||
name = serializers.CharField()
|
||||
slug = serializers.CharField()
|
||||
latitude = serializers.FloatField(allow_null=True)
|
||||
longitude = serializers.FloatField(allow_null=True)
|
||||
status = serializers.CharField()
|
||||
|
||||
# Location details
|
||||
location = serializers.SerializerMethodField()
|
||||
|
||||
# Statistics
|
||||
stats = serializers.SerializerMethodField()
|
||||
|
||||
@extend_schema_field(serializers.DictField())
|
||||
def get_location(self, obj) -> dict:
|
||||
"""Get location information."""
|
||||
if hasattr(obj, 'location') and obj.location:
|
||||
return {
|
||||
"city": obj.location.city,
|
||||
"state": obj.location.state,
|
||||
"country": obj.location.country,
|
||||
"formatted_address": obj.location.formatted_address,
|
||||
}
|
||||
return {}
|
||||
|
||||
@extend_schema_field(serializers.DictField())
|
||||
def get_stats(self, obj) -> dict:
|
||||
"""Get relevant statistics based on object type."""
|
||||
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,
|
||||
}
|
||||
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,
|
||||
"park_name": obj.park.name if obj.park else None,
|
||||
}
|
||||
return {}
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Map Cluster Example",
|
||||
summary="Example map cluster response",
|
||||
description="A cluster of locations on the map",
|
||||
value={
|
||||
"id": "cluster_1",
|
||||
"type": "cluster",
|
||||
"latitude": 41.5,
|
||||
"longitude": -82.7,
|
||||
"count": 5,
|
||||
"bounds": {
|
||||
"north": 41.6,
|
||||
"south": 41.4,
|
||||
"east": -82.6,
|
||||
"west": -82.8,
|
||||
},
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class MapClusterSerializer(serializers.Serializer):
|
||||
"""Serializer for map clusters."""
|
||||
|
||||
id = serializers.CharField()
|
||||
type = serializers.CharField(default="cluster")
|
||||
latitude = serializers.FloatField()
|
||||
longitude = serializers.FloatField()
|
||||
count = serializers.IntegerField()
|
||||
bounds = serializers.DictField()
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Map Locations Response Example",
|
||||
summary="Example map locations response",
|
||||
description="Response containing locations and optional clusters",
|
||||
value={
|
||||
"status": "success",
|
||||
"data": {
|
||||
"locations": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "park",
|
||||
"name": "Cedar Point",
|
||||
"slug": "cedar-point",
|
||||
"latitude": 41.4793,
|
||||
"longitude": -82.6833,
|
||||
"status": "OPERATING",
|
||||
}
|
||||
],
|
||||
"clusters": [],
|
||||
"bounds": {
|
||||
"north": 41.5,
|
||||
"south": 41.4,
|
||||
"east": -82.6,
|
||||
"west": -82.8,
|
||||
},
|
||||
"total_count": 1,
|
||||
"clustered": False,
|
||||
},
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class MapLocationsResponseSerializer(serializers.Serializer):
|
||||
"""Response serializer for map locations endpoint."""
|
||||
|
||||
status = serializers.CharField(default="success")
|
||||
locations = serializers.ListField(child=serializers.DictField())
|
||||
clusters = serializers.ListField(child=serializers.DictField(), default=list)
|
||||
bounds = serializers.DictField(default=dict)
|
||||
total_count = serializers.IntegerField(default=0)
|
||||
clustered = serializers.BooleanField(default=False)
|
||||
|
||||
|
||||
# === MAP SEARCH SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Map Search Result Example",
|
||||
summary="Example map search result",
|
||||
description="A search result for map locations",
|
||||
value={
|
||||
"id": 1,
|
||||
"type": "park",
|
||||
"name": "Cedar Point",
|
||||
"slug": "cedar-point",
|
||||
"latitude": 41.4793,
|
||||
"longitude": -82.6833,
|
||||
"location": {
|
||||
"city": "Sandusky",
|
||||
"state": "Ohio",
|
||||
"country": "United States",
|
||||
},
|
||||
"relevance_score": 0.95,
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class MapSearchResultSerializer(serializers.Serializer):
|
||||
"""Serializer for map search results."""
|
||||
|
||||
id = serializers.IntegerField()
|
||||
type = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
slug = serializers.CharField()
|
||||
latitude = serializers.FloatField(allow_null=True)
|
||||
longitude = serializers.FloatField(allow_null=True)
|
||||
location = serializers.SerializerMethodField()
|
||||
relevance_score = serializers.FloatField(required=False)
|
||||
|
||||
@extend_schema_field(serializers.DictField())
|
||||
def get_location(self, obj) -> dict:
|
||||
"""Get location information."""
|
||||
if hasattr(obj, 'location') and obj.location:
|
||||
return {
|
||||
"city": obj.location.city,
|
||||
"state": obj.location.state,
|
||||
"country": obj.location.country,
|
||||
}
|
||||
return {}
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Map Search Response Example",
|
||||
summary="Example map search response",
|
||||
description="Response containing search results",
|
||||
value={
|
||||
"status": "success",
|
||||
"data": {
|
||||
"results": [
|
||||
{
|
||||
"id": 1,
|
||||
"type": "park",
|
||||
"name": "Cedar Point",
|
||||
"slug": "cedar-point",
|
||||
"latitude": 41.4793,
|
||||
"longitude": -82.6833,
|
||||
}
|
||||
],
|
||||
"query": "cedar point",
|
||||
"total_count": 1,
|
||||
"page": 1,
|
||||
"page_size": 20,
|
||||
},
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class MapSearchResponseSerializer(serializers.Serializer):
|
||||
"""Response serializer for map search endpoint."""
|
||||
|
||||
status = serializers.CharField(default="success")
|
||||
results = serializers.ListField(child=serializers.DictField())
|
||||
query = serializers.CharField()
|
||||
total_count = serializers.IntegerField(default=0)
|
||||
page = serializers.IntegerField(default=1)
|
||||
page_size = serializers.IntegerField(default=20)
|
||||
|
||||
|
||||
# === MAP DETAIL SERIALIZERS ===
|
||||
|
||||
|
||||
@extend_schema_serializer(
|
||||
examples=[
|
||||
OpenApiExample(
|
||||
"Map Location Detail Example",
|
||||
summary="Example map location detail response",
|
||||
description="Detailed information about a specific location",
|
||||
value={
|
||||
"id": 1,
|
||||
"type": "park",
|
||||
"name": "Cedar Point",
|
||||
"slug": "cedar-point",
|
||||
"description": "America's Roller Coast",
|
||||
"latitude": 41.4793,
|
||||
"longitude": -82.6833,
|
||||
"status": "OPERATING",
|
||||
"location": {
|
||||
"street_address": "1 Cedar Point Dr",
|
||||
"city": "Sandusky",
|
||||
"state": "Ohio",
|
||||
"country": "United States",
|
||||
"postal_code": "44870",
|
||||
"formatted_address": "1 Cedar Point Dr, Sandusky, Ohio, 44870, United States",
|
||||
},
|
||||
"stats": {
|
||||
"coaster_count": 17,
|
||||
"ride_count": 70,
|
||||
"average_rating": 4.5,
|
||||
},
|
||||
"nearby_locations": [],
|
||||
},
|
||||
)
|
||||
]
|
||||
)
|
||||
class MapLocationDetailSerializer(serializers.Serializer):
|
||||
"""Serializer for detailed map location information."""
|
||||
|
||||
id = serializers.IntegerField()
|
||||
type = serializers.CharField()
|
||||
name = serializers.CharField()
|
||||
slug = serializers.CharField()
|
||||
description = serializers.CharField()
|
||||
latitude = serializers.FloatField(allow_null=True)
|
||||
longitude = serializers.FloatField(allow_null=True)
|
||||
status = serializers.CharField()
|
||||
|
||||
# Detailed location information
|
||||
location = serializers.SerializerMethodField()
|
||||
|
||||
# Statistics
|
||||
stats = serializers.SerializerMethodField()
|
||||
|
||||
# Nearby locations
|
||||
nearby_locations = serializers.SerializerMethodField()
|
||||
|
||||
@extend_schema_field(serializers.DictField())
|
||||
def get_location(self, obj) -> dict:
|
||||
"""Get detailed location information."""
|
||||
if hasattr(obj, 'location') and obj.location:
|
||||
return {
|
||||
"street_address": obj.location.street_address,
|
||||
"city": obj.location.city,
|
||||
"state": obj.location.state,
|
||||
"country": obj.location.country,
|
||||
"postal_code": obj.location.postal_code,
|
||||
"formatted_address": obj.location.formatted_address,
|
||||
}
|
||||
return {}
|
||||
|
||||
@extend_schema_field(serializers.DictField())
|
||||
def get_stats(self, obj) -> dict:
|
||||
"""Get detailed statistics based on object type."""
|
||||
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,
|
||||
"size_acres": float(obj.size_acres) if obj.size_acres else None,
|
||||
"opening_date": obj.opening_date.isoformat() if obj.opening_date else None,
|
||||
}
|
||||
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,
|
||||
"park_name": obj.park.name if obj.park else None,
|
||||
"opening_date": obj.opening_date.isoformat() if obj.opening_date else None,
|
||||
"manufacturer": obj.manufacturer.name if obj.manufacturer else None,
|
||||
}
|
||||
return {}
|
||||
|
||||
@extend_schema_field(serializers.ListField(child=serializers.DictField()))
|
||||
def get_nearby_locations(self, obj) -> list:
|
||||
"""Get nearby locations (placeholder for now)."""
|
||||
# TODO: Implement nearby location logic
|
||||
return []
|
||||
|
||||
|
||||
# === INPUT SERIALIZERS ===
|
||||
|
||||
|
||||
class MapBoundsInputSerializer(serializers.Serializer):
|
||||
"""Input serializer for map bounds queries."""
|
||||
|
||||
north = serializers.FloatField(min_value=-90, max_value=90)
|
||||
south = serializers.FloatField(min_value=-90, max_value=90)
|
||||
east = serializers.FloatField(min_value=-180, max_value=180)
|
||||
west = serializers.FloatField(min_value=-180, max_value=180)
|
||||
|
||||
def validate(self, attrs):
|
||||
"""Validate that bounds make geographic sense."""
|
||||
if attrs['north'] <= attrs['south']:
|
||||
raise serializers.ValidationError(
|
||||
"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']:
|
||||
raise serializers.ValidationError("West bound must be less than east bound")
|
||||
|
||||
return attrs
|
||||
|
||||
|
||||
class MapSearchInputSerializer(serializers.Serializer):
|
||||
"""Input serializer for map search queries."""
|
||||
|
||||
q = serializers.CharField(min_length=1, max_length=255)
|
||||
types = serializers.CharField(required=False, allow_blank=True)
|
||||
bounds = MapBoundsInputSerializer(required=False)
|
||||
page = serializers.IntegerField(min_value=1, default=1)
|
||||
page_size = serializers.IntegerField(min_value=1, max_value=100, default=20)
|
||||
|
||||
def validate_types(self, value):
|
||||
"""Validate location types."""
|
||||
if not value:
|
||||
return []
|
||||
|
||||
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:
|
||||
raise serializers.ValidationError(
|
||||
f"Invalid location type: {location_type}. Valid types: {', '.join(valid_types)}"
|
||||
)
|
||||
|
||||
return types
|
||||
155
backend/apps/api/v1/serializers/stats.py
Normal file
155
backend/apps/api/v1/serializers/stats.py
Normal file
@@ -0,0 +1,155 @@
|
||||
"""
|
||||
Statistics serializers for ThrillWiki API.
|
||||
|
||||
Provides serialization for platform statistics data.
|
||||
"""
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class StatsSerializer(serializers.Serializer):
|
||||
"""
|
||||
Serializer for platform statistics response.
|
||||
|
||||
This serializer defines the structure of the statistics API response,
|
||||
including all the various counts and breakdowns available.
|
||||
"""
|
||||
|
||||
# Core entity counts
|
||||
total_parks = serializers.IntegerField(
|
||||
help_text="Total number of parks in the database"
|
||||
)
|
||||
total_rides = serializers.IntegerField(
|
||||
help_text="Total number of rides in the database"
|
||||
)
|
||||
total_manufacturers = serializers.IntegerField(
|
||||
help_text="Total number of ride manufacturers"
|
||||
)
|
||||
total_operators = serializers.IntegerField(
|
||||
help_text="Total number of park operators"
|
||||
)
|
||||
total_designers = serializers.IntegerField(
|
||||
help_text="Total number of ride designers"
|
||||
)
|
||||
total_property_owners = serializers.IntegerField(
|
||||
help_text="Total number of property owners"
|
||||
)
|
||||
total_roller_coasters = serializers.IntegerField(
|
||||
help_text="Total number of roller coasters with detailed stats"
|
||||
)
|
||||
|
||||
# Photo counts
|
||||
total_photos = serializers.IntegerField(
|
||||
help_text="Total number of photos (parks + rides combined)"
|
||||
)
|
||||
total_park_photos = serializers.IntegerField(
|
||||
help_text="Total number of park photos"
|
||||
)
|
||||
total_ride_photos = serializers.IntegerField(
|
||||
help_text="Total number of ride photos"
|
||||
)
|
||||
|
||||
# Review counts
|
||||
total_reviews = serializers.IntegerField(
|
||||
help_text="Total number of reviews (parks + rides)"
|
||||
)
|
||||
total_park_reviews = serializers.IntegerField(
|
||||
help_text="Total number of park reviews"
|
||||
)
|
||||
total_ride_reviews = serializers.IntegerField(
|
||||
help_text="Total number of ride reviews"
|
||||
)
|
||||
|
||||
# Ride category counts (optional fields since they depend on data)
|
||||
roller_coasters = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides categorized as roller coasters"
|
||||
)
|
||||
dark_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides categorized as dark rides"
|
||||
)
|
||||
flat_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides categorized as flat rides"
|
||||
)
|
||||
water_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides categorized as water rides"
|
||||
)
|
||||
transport_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides categorized as transport rides"
|
||||
)
|
||||
other_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides categorized as other"
|
||||
)
|
||||
|
||||
# Park status counts (optional fields since they depend on data)
|
||||
operating_parks = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of currently operating parks"
|
||||
)
|
||||
temporarily_closed_parks = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of temporarily closed parks"
|
||||
)
|
||||
permanently_closed_parks = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of permanently closed parks"
|
||||
)
|
||||
under_construction_parks = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of parks under construction"
|
||||
)
|
||||
demolished_parks = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of demolished parks"
|
||||
)
|
||||
relocated_parks = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of relocated parks"
|
||||
)
|
||||
|
||||
# Ride status counts (optional fields since they depend on data)
|
||||
operating_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of currently operating rides"
|
||||
)
|
||||
temporarily_closed_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of temporarily closed rides"
|
||||
)
|
||||
sbno_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides standing but not operating"
|
||||
)
|
||||
closing_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides in the process of closing"
|
||||
)
|
||||
permanently_closed_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of permanently closed rides"
|
||||
)
|
||||
under_construction_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of rides under construction"
|
||||
)
|
||||
demolished_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of demolished rides"
|
||||
)
|
||||
relocated_rides = serializers.IntegerField(
|
||||
required=False,
|
||||
help_text="Number of relocated rides"
|
||||
)
|
||||
|
||||
# Metadata
|
||||
last_updated = serializers.CharField(
|
||||
help_text="ISO timestamp when these statistics were last calculated"
|
||||
)
|
||||
relative_last_updated = serializers.CharField(
|
||||
help_text="Human-readable relative time since last update (e.g., '2 minutes ago')"
|
||||
)
|
||||
Reference in New Issue
Block a user