mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:31:08 -05:00
- 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.
333 lines
10 KiB
Python
333 lines
10 KiB
Python
"""
|
|
Schema extensions and customizations for drf-spectacular.
|
|
|
|
This module provides custom extensions to improve OpenAPI schema generation
|
|
for the ThrillWiki API, including better documentation and examples.
|
|
"""
|
|
|
|
from drf_spectacular.openapi import AutoSchema
|
|
|
|
|
|
# Custom examples for common serializers
|
|
|
|
PARK_EXAMPLE = {
|
|
"id": 1,
|
|
"name": "Cedar Point",
|
|
"slug": "cedar-point",
|
|
"description": "The Roller Coaster Capital of the World",
|
|
"status": "OPERATING",
|
|
"opening_date": "1870-07-04",
|
|
"closing_date": None,
|
|
"location": {
|
|
"latitude": 41.4793,
|
|
"longitude": -82.6833,
|
|
"city": "Sandusky",
|
|
"state": "Ohio",
|
|
"country": "United States",
|
|
"formatted_address": "Sandusky, OH, United States",
|
|
},
|
|
"operator": {
|
|
"id": 1,
|
|
"name": "Cedar Fair",
|
|
"slug": "cedar-fair",
|
|
"roles": ["OPERATOR", "PROPERTY_OWNER"],
|
|
},
|
|
"property_owner": {
|
|
"id": 1,
|
|
"name": "Cedar Fair",
|
|
"slug": "cedar-fair",
|
|
"roles": ["OPERATOR", "PROPERTY_OWNER"],
|
|
},
|
|
"area_count": 15,
|
|
"ride_count": 70,
|
|
"operating_rides_count": 68,
|
|
"roller_coaster_count": 17,
|
|
}
|
|
|
|
RIDE_EXAMPLE = {
|
|
"id": 1,
|
|
"name": "Steel Vengeance",
|
|
"slug": "steel-vengeance",
|
|
"description": "A hybrid wooden/steel roller coaster",
|
|
"category": "ROLLER_COASTER",
|
|
"status": "OPERATING",
|
|
"opening_date": "2018-05-05",
|
|
"closing_date": None,
|
|
"park": {"id": 1, "name": "Cedar Point", "slug": "cedar-point"},
|
|
"manufacturer": {
|
|
"id": 1,
|
|
"name": "Rocky Mountain Construction",
|
|
"slug": "rmc",
|
|
"roles": ["MANUFACTURER"],
|
|
},
|
|
"designer": {
|
|
"id": 1,
|
|
"name": "Rocky Mountain Construction",
|
|
"slug": "rmc",
|
|
"roles": ["DESIGNER"],
|
|
},
|
|
"height_feet": 205,
|
|
"length_feet": 5740,
|
|
"speed_mph": 74,
|
|
"inversions": 4,
|
|
"duration_seconds": 150,
|
|
"capacity_per_hour": 1200,
|
|
"minimum_height_inches": 48,
|
|
"maximum_height_inches": None,
|
|
}
|
|
|
|
COMPANY_EXAMPLE = {
|
|
"id": 1,
|
|
"name": "Cedar Fair",
|
|
"slug": "cedar-fair",
|
|
"roles": ["OPERATOR", "PROPERTY_OWNER"],
|
|
}
|
|
|
|
LOCATION_EXAMPLE = {
|
|
"latitude": 41.4793,
|
|
"longitude": -82.6833,
|
|
"city": "Sandusky",
|
|
"state": "Ohio",
|
|
"country": "United States",
|
|
"formatted_address": "Sandusky, OH, United States",
|
|
}
|
|
|
|
HISTORY_EVENT_EXAMPLE = {
|
|
"id": "12345678-1234-5678-9012-123456789012",
|
|
"pgh_created_at": "2024-01-15T14:30:00Z",
|
|
"pgh_label": "updated",
|
|
"pgh_model": "parks.park",
|
|
"pgh_obj_id": 1,
|
|
"pgh_context": {
|
|
"user_id": 42,
|
|
"request_id": "req_abc123",
|
|
"ip_address": "192.168.1.100",
|
|
},
|
|
"changed_fields": ["name", "description"],
|
|
"field_changes": {
|
|
"name": {"old_value": "Cedar Point Amusement Park", "new_value": "Cedar Point"},
|
|
"description": {
|
|
"old_value": "America's Roller Coast",
|
|
"new_value": "The Roller Coaster Capital of the World",
|
|
},
|
|
},
|
|
}
|
|
|
|
PARK_HISTORY_EXAMPLE = {
|
|
"park": PARK_EXAMPLE,
|
|
"current_state": PARK_EXAMPLE,
|
|
"summary": {
|
|
"total_events": 25,
|
|
"first_recorded": "2023-01-01T00:00:00Z",
|
|
"last_modified": "2024-01-15T14:30:00Z",
|
|
"significant_changes": [
|
|
{
|
|
"date": "2024-01-15T14:30:00Z",
|
|
"event_type": "updated",
|
|
"description": "Name and description updated",
|
|
},
|
|
{
|
|
"date": "2023-06-01T10:00:00Z",
|
|
"event_type": "updated",
|
|
"description": "Operating status changed",
|
|
},
|
|
],
|
|
},
|
|
"events": [HISTORY_EVENT_EXAMPLE],
|
|
}
|
|
|
|
UNIFIED_HISTORY_TIMELINE_EXAMPLE = {
|
|
"summary": {
|
|
"total_events": 1250,
|
|
"events_returned": 100,
|
|
"event_type_breakdown": {"created": 45, "updated": 180, "deleted": 5},
|
|
"model_type_breakdown": {
|
|
"parks.park": 75,
|
|
"rides.ride": 120,
|
|
"companies.operator": 15,
|
|
"companies.manufacturer": 25,
|
|
"accounts.user": 30,
|
|
},
|
|
"time_range": {
|
|
"earliest": "2023-01-01T00:00:00Z",
|
|
"latest": "2024-01-15T14:30:00Z",
|
|
},
|
|
},
|
|
"events": [
|
|
{
|
|
"id": "event_001",
|
|
"pgh_created_at": "2024-01-15T14:30:00Z",
|
|
"pgh_label": "updated",
|
|
"pgh_model": "parks.park",
|
|
"pgh_obj_id": 1,
|
|
"entity_name": "Cedar Point",
|
|
"entity_slug": "cedar-point",
|
|
"change_significance": "minor",
|
|
"change_summary": "Park description updated",
|
|
},
|
|
{
|
|
"id": "event_002",
|
|
"pgh_created_at": "2024-01-15T12:00:00Z",
|
|
"pgh_label": "created",
|
|
"pgh_model": "rides.ride",
|
|
"pgh_obj_id": 100,
|
|
"entity_name": "New Roller Coaster",
|
|
"entity_slug": "new-roller-coaster",
|
|
"change_significance": "major",
|
|
"change_summary": "New ride added to park",
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
# OpenAPI schema customizations
|
|
|
|
|
|
def custom_preprocessing_hook(endpoints):
|
|
"""
|
|
Custom preprocessing hook to modify endpoints before schema generation.
|
|
|
|
This can be used to filter out certain endpoints, modify their metadata,
|
|
or add custom documentation.
|
|
"""
|
|
# Filter out any endpoints we don't want in the public API
|
|
filtered = []
|
|
for path, path_regex, method, callback in endpoints:
|
|
# Skip internal or debug endpoints
|
|
if "/debug/" not in path and "/internal/" not in path:
|
|
filtered.append((path, path_regex, method, callback))
|
|
|
|
return filtered
|
|
|
|
|
|
def custom_postprocessing_hook(result, generator, request, public):
|
|
"""
|
|
Custom postprocessing hook to modify the generated schema.
|
|
|
|
This can be used to add custom metadata, modify response schemas,
|
|
or enhance the overall API documentation.
|
|
"""
|
|
# Add custom info to the schema
|
|
if "info" in result:
|
|
result["info"]["contact"] = {
|
|
"name": "ThrillWiki API Support",
|
|
"email": "api@thrillwiki.com",
|
|
"url": "https://thrillwiki.com/support",
|
|
}
|
|
|
|
result["info"]["license"] = {
|
|
"name": "MIT",
|
|
"url": "https://opensource.org/licenses/MIT",
|
|
}
|
|
|
|
# Add custom tags with descriptions
|
|
if "tags" not in result:
|
|
result["tags"] = []
|
|
|
|
result["tags"].extend(
|
|
[
|
|
{
|
|
"name": "Parks",
|
|
"description": "Operations related to theme parks, including CRUD operations and statistics",
|
|
},
|
|
{
|
|
"name": "Rides",
|
|
"description": "Operations related to rides and attractions within theme parks",
|
|
},
|
|
{
|
|
"name": "History",
|
|
"description": "Historical change tracking for all entities, providing complete audit trails and version history",
|
|
"externalDocs": {
|
|
"description": "Learn more about pghistory",
|
|
"url": "https://django-pghistory.readthedocs.io/",
|
|
},
|
|
},
|
|
{
|
|
"name": "Statistics",
|
|
"description": "Statistical endpoints providing aggregated data and insights",
|
|
},
|
|
{
|
|
"name": "Reviews",
|
|
"description": "User reviews and ratings for parks and rides",
|
|
},
|
|
{
|
|
"name": "Authentication",
|
|
"description": "User authentication and account management endpoints",
|
|
},
|
|
{
|
|
"name": "Health",
|
|
"description": "System health checks and monitoring endpoints",
|
|
},
|
|
{
|
|
"name": "Recent Changes",
|
|
"description": "Endpoints for accessing recently changed entities by type and change category",
|
|
},
|
|
]
|
|
)
|
|
|
|
# Add custom servers if not present
|
|
if "servers" not in result:
|
|
result["servers"] = [
|
|
{
|
|
"url": "https://api.thrillwiki.com/v1",
|
|
"description": "Production server",
|
|
},
|
|
{
|
|
"url": "https://staging-api.thrillwiki.com/v1",
|
|
"description": "Staging server",
|
|
},
|
|
{
|
|
"url": "http://localhost:8000/api/v1",
|
|
"description": "Development server",
|
|
},
|
|
]
|
|
|
|
return result
|
|
|
|
|
|
# Custom AutoSchema class for enhanced documentation
|
|
class ThrillWikiAutoSchema(AutoSchema):
|
|
"""
|
|
Custom AutoSchema class that provides enhanced documentation
|
|
for ThrillWiki API endpoints.
|
|
"""
|
|
|
|
def get_operation_id(self):
|
|
"""Generate meaningful operation IDs."""
|
|
if hasattr(self.view, "basename"):
|
|
basename = self.view.basename
|
|
else:
|
|
basename = getattr(self.view, "__class__", self.view).__name__.lower()
|
|
if basename.endswith("viewset"):
|
|
basename = basename[:-7] # Remove 'viewset' suffix
|
|
|
|
action = self.method_mapping.get(self.method.lower(), self.method.lower())
|
|
return f"{basename}_{action}"
|
|
|
|
def get_tags(self):
|
|
"""Generate tags based on the viewset."""
|
|
if hasattr(self.view, "basename"):
|
|
return [self.view.basename.title()]
|
|
return super().get_tags()
|
|
|
|
def get_summary(self):
|
|
"""Generate summary from docstring or method name."""
|
|
summary = super().get_summary()
|
|
if summary:
|
|
return summary
|
|
|
|
# Generate from method and model
|
|
action = self.method_mapping.get(self.method.lower(), self.method.lower())
|
|
model_name = getattr(self.view, "basename", "resource")
|
|
|
|
action_map = {
|
|
"list": f"List {model_name}",
|
|
"create": f"Create {model_name}",
|
|
"retrieve": f"Get {model_name} details",
|
|
"update": f"Update {model_name}",
|
|
"partial_update": f"Partially update {model_name}",
|
|
"destroy": f"Delete {model_name}",
|
|
}
|
|
|
|
return action_map.get(action, f"{action.title()} {model_name}")
|