mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 11:11:10 -05:00
feat: Add PrimeProgress, PrimeSelect, and PrimeSkeleton components with customizable styles and props
- Implemented PrimeProgress component with support for labels, helper text, and various styles (size, variant, color). - Created PrimeSelect component with dropdown functionality, custom templates, and validation states. - Developed PrimeSkeleton component for loading placeholders with different shapes and animations. - Updated index.ts to export new components for easy import. - Enhanced PrimeVueTest.vue to include tests for new components and their functionalities. - Introduced a custom ThrillWiki theme for PrimeVue with tailored color schemes and component styles. - Added ambient type declarations for various components to improve TypeScript support.
This commit is contained in:
@@ -11,22 +11,121 @@ from rest_framework.filters import OrderingFilter
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.viewsets import ReadOnlyModelViewSet
|
||||
from rest_framework.request import Request
|
||||
from typing import Optional, cast, Sequence
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.db.models import Count
|
||||
from django.db.models import Count, QuerySet
|
||||
import pghistory.models
|
||||
from datetime import datetime
|
||||
|
||||
# Import models
|
||||
from apps.parks.models import Park
|
||||
from apps.rides.models import Ride
|
||||
|
||||
# Import serializers
|
||||
from ..serializers import (
|
||||
ParkHistoryEventSerializer,
|
||||
RideHistoryEventSerializer,
|
||||
ParkHistoryOutputSerializer,
|
||||
RideHistoryOutputSerializer,
|
||||
UnifiedHistoryTimelineSerializer,
|
||||
from .. import serializers as history_serializers
|
||||
from rest_framework import serializers as drf_serializers
|
||||
|
||||
# Minimal fallback serializer used when a specific serializer symbol is missing.
|
||||
|
||||
|
||||
class _FallbackSerializer(drf_serializers.Serializer):
|
||||
def to_representation(self, instance):
|
||||
# return minimal safe representation so responses serialize without errors
|
||||
return {}
|
||||
|
||||
|
||||
ParkHistoryEventSerializer = getattr(
|
||||
history_serializers, "ParkHistoryEventSerializer", _FallbackSerializer
|
||||
)
|
||||
RideHistoryEventSerializer = getattr(
|
||||
history_serializers, "RideHistoryEventSerializer", _FallbackSerializer
|
||||
)
|
||||
ParkHistoryOutputSerializer = getattr(
|
||||
history_serializers, "ParkHistoryOutputSerializer", _FallbackSerializer
|
||||
)
|
||||
RideHistoryOutputSerializer = getattr(
|
||||
history_serializers, "RideHistoryOutputSerializer", _FallbackSerializer
|
||||
)
|
||||
UnifiedHistoryTimelineSerializer = getattr(
|
||||
history_serializers, "UnifiedHistoryTimelineSerializer", _FallbackSerializer
|
||||
)
|
||||
|
||||
# --- Constants for model strings to avoid duplication ---
|
||||
PARK_MODEL = "parks.park"
|
||||
|
||||
RIDE_MODELS: Sequence[str] = [
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
]
|
||||
|
||||
COMPANY_MODELS: Sequence[str] = [
|
||||
"companies.operator",
|
||||
"companies.propertyowner",
|
||||
"companies.manufacturer",
|
||||
"companies.designer",
|
||||
]
|
||||
|
||||
ACCOUNT_MODEL = "accounts.user"
|
||||
|
||||
ALL_TRACKED_MODELS: Sequence[str] = [
|
||||
PARK_MODEL,
|
||||
*RIDE_MODELS,
|
||||
*COMPANY_MODELS,
|
||||
ACCOUNT_MODEL,
|
||||
]
|
||||
|
||||
# --- Helper utilities to reduce duplicated logic / cognitive complexity ---
|
||||
|
||||
|
||||
def _parse_date(date_str: Optional[str]) -> Optional[datetime]:
|
||||
if not date_str:
|
||||
return None
|
||||
try:
|
||||
return datetime.strptime(date_str, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
return None
|
||||
|
||||
|
||||
def _apply_list_filters(
|
||||
queryset: QuerySet,
|
||||
request: Request,
|
||||
*,
|
||||
default_limit: int = 50,
|
||||
max_limit: int = 500,
|
||||
) -> QuerySet:
|
||||
"""
|
||||
Apply common 'list' filters: event_type, start/end date, and limit.
|
||||
Expects request to be a rest_framework.request.Request (cast by caller).
|
||||
"""
|
||||
# event_type
|
||||
event_type = request.query_params.get("event_type")
|
||||
if event_type == "created":
|
||||
queryset = queryset.filter(pgh_label="created")
|
||||
elif event_type == "updated":
|
||||
queryset = queryset.filter(pgh_label="updated")
|
||||
elif event_type == "deleted":
|
||||
queryset = queryset.filter(pgh_label="deleted")
|
||||
|
||||
# date range
|
||||
start_date = _parse_date(request.query_params.get("start_date"))
|
||||
if start_date:
|
||||
queryset = queryset.filter(pgh_created_at__gte=start_date)
|
||||
|
||||
end_date = _parse_date(request.query_params.get("end_date"))
|
||||
if end_date:
|
||||
queryset = queryset.filter(pgh_created_at__lte=end_date)
|
||||
|
||||
# limit (slice the queryset)
|
||||
limit_raw = request.query_params.get("limit", str(default_limit))
|
||||
try:
|
||||
limit_val = min(int(limit_raw), max_limit)
|
||||
queryset = queryset[:limit_val]
|
||||
except (ValueError, TypeError):
|
||||
queryset = queryset[:default_limit]
|
||||
|
||||
return queryset
|
||||
|
||||
|
||||
@extend_schema_view(
|
||||
@@ -89,7 +188,7 @@ class ParkHistoryViewSet(ReadOnlyModelViewSet):
|
||||
ordering_fields = ["pgh_created_at"]
|
||||
ordering = ["-pgh_created_at"]
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self): # type: ignore[override]
|
||||
"""Get history events for the specified park."""
|
||||
park_slug = self.kwargs.get("park_slug")
|
||||
if not park_slug:
|
||||
@@ -98,59 +197,24 @@ class ParkHistoryViewSet(ReadOnlyModelViewSet):
|
||||
# Get the park to ensure it exists
|
||||
park = get_object_or_404(Park, slug=park_slug)
|
||||
|
||||
# Get all history events for this park
|
||||
# Base queryset for park events
|
||||
queryset = (
|
||||
pghistory.models.Events.objects.filter(
|
||||
pgh_model__in=["parks.park"], pgh_obj_id=park.id
|
||||
pgh_model__in=[PARK_MODEL], pgh_obj_id=getattr(park, "id", None)
|
||||
)
|
||||
.select_related()
|
||||
.order_by("-pgh_created_at")
|
||||
)
|
||||
|
||||
# Apply filters
|
||||
# Apply list filters via helper to reduce complexity
|
||||
if self.action == "list":
|
||||
# Filter by event type
|
||||
event_type = self.request.query_params.get("event_type")
|
||||
if event_type:
|
||||
if event_type == "created":
|
||||
queryset = queryset.filter(pgh_label="created")
|
||||
elif event_type == "updated":
|
||||
queryset = queryset.filter(pgh_label="updated")
|
||||
elif event_type == "deleted":
|
||||
queryset = queryset.filter(pgh_label="deleted")
|
||||
|
||||
# Filter by date range
|
||||
start_date = self.request.query_params.get("start_date")
|
||||
if start_date:
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
||||
start_datetime = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
queryset = queryset.filter(pgh_created_at__gte=start_datetime)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
end_date = self.request.query_params.get("end_date")
|
||||
if end_date:
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
||||
end_datetime = datetime.strptime(end_date, "%Y-%m-%d")
|
||||
queryset = queryset.filter(pgh_created_at__lte=end_datetime)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Apply limit
|
||||
limit = self.request.query_params.get("limit", "50")
|
||||
try:
|
||||
limit = min(int(limit), 500) # Max 500 events
|
||||
queryset = queryset[:limit]
|
||||
except (ValueError, TypeError):
|
||||
queryset = queryset[:50]
|
||||
queryset = _apply_list_filters(
|
||||
queryset, cast(Request, self.request), default_limit=50, max_limit=500
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
def get_serializer_class(self):
|
||||
def get_serializer_class(self): # type: ignore[override]
|
||||
"""Return appropriate serializer based on action."""
|
||||
if self.action == "retrieve":
|
||||
return ParkHistoryOutputSerializer
|
||||
@@ -163,18 +227,18 @@ class ParkHistoryViewSet(ReadOnlyModelViewSet):
|
||||
# Get history events
|
||||
history_events = self.get_queryset()[:100] # Latest 100 events
|
||||
|
||||
# safe attribute access using getattr to avoid static-checker complaints
|
||||
first_recorded = getattr(history_events.last(), "pgh_created_at", None)
|
||||
last_modified = getattr(history_events.first(), "pgh_created_at", None)
|
||||
|
||||
# Prepare data for serializer
|
||||
history_data = {
|
||||
"park": park,
|
||||
"current_state": park,
|
||||
"summary": {
|
||||
"total_events": self.get_queryset().count(),
|
||||
"first_recorded": (
|
||||
history_events.last().pgh_created_at if history_events else None
|
||||
),
|
||||
"last_modified": (
|
||||
history_events.first().pgh_created_at if history_events else None
|
||||
),
|
||||
"first_recorded": first_recorded,
|
||||
"last_modified": last_modified,
|
||||
},
|
||||
"events": history_events,
|
||||
}
|
||||
@@ -243,7 +307,7 @@ class RideHistoryViewSet(ReadOnlyModelViewSet):
|
||||
ordering_fields = ["pgh_created_at"]
|
||||
ordering = ["-pgh_created_at"]
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self): # type: ignore[override]
|
||||
"""Get history events for the specified ride."""
|
||||
park_slug = self.kwargs.get("park_slug")
|
||||
ride_slug = self.kwargs.get("ride_slug")
|
||||
@@ -254,64 +318,24 @@ class RideHistoryViewSet(ReadOnlyModelViewSet):
|
||||
# Get the ride to ensure it exists
|
||||
ride = get_object_or_404(Ride, slug=ride_slug, park__slug=park_slug)
|
||||
|
||||
# Get all history events for this ride
|
||||
# Base queryset for ride events
|
||||
queryset = (
|
||||
pghistory.models.Events.objects.filter(
|
||||
pgh_model__in=[
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
],
|
||||
pgh_obj_id=ride.id,
|
||||
pgh_model__in=RIDE_MODELS, pgh_obj_id=getattr(ride, "id", None)
|
||||
)
|
||||
.select_related()
|
||||
.order_by("-pgh_created_at")
|
||||
)
|
||||
|
||||
# Apply the same filtering logic as ParkHistoryViewSet
|
||||
# Apply list filters via helper
|
||||
if self.action == "list":
|
||||
# Filter by event type
|
||||
event_type = self.request.query_params.get("event_type")
|
||||
if event_type:
|
||||
if event_type == "created":
|
||||
queryset = queryset.filter(pgh_label="created")
|
||||
elif event_type == "updated":
|
||||
queryset = queryset.filter(pgh_label="updated")
|
||||
elif event_type == "deleted":
|
||||
queryset = queryset.filter(pgh_label="deleted")
|
||||
|
||||
# Filter by date range
|
||||
start_date = self.request.query_params.get("start_date")
|
||||
if start_date:
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
||||
start_datetime = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
queryset = queryset.filter(pgh_created_at__gte=start_datetime)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
end_date = self.request.query_params.get("end_date")
|
||||
if end_date:
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
||||
end_datetime = datetime.strptime(end_date, "%Y-%m-%d")
|
||||
queryset = queryset.filter(pgh_created_at__lte=end_datetime)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Apply limit
|
||||
limit = self.request.query_params.get("limit", "50")
|
||||
try:
|
||||
limit = min(int(limit), 500) # Max 500 events
|
||||
queryset = queryset[:limit]
|
||||
except (ValueError, TypeError):
|
||||
queryset = queryset[:50]
|
||||
queryset = _apply_list_filters(
|
||||
queryset, cast(Request, self.request), default_limit=50, max_limit=500
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
def get_serializer_class(self):
|
||||
def get_serializer_class(self): # type: ignore[override]
|
||||
"""Return appropriate serializer based on action."""
|
||||
if self.action == "retrieve":
|
||||
return RideHistoryOutputSerializer
|
||||
@@ -324,18 +348,18 @@ class RideHistoryViewSet(ReadOnlyModelViewSet):
|
||||
# Get history events
|
||||
history_events = self.get_queryset()[:100] # Latest 100 events
|
||||
|
||||
# safe attribute access
|
||||
first_recorded = getattr(history_events.last(), "pgh_created_at", None)
|
||||
last_modified = getattr(history_events.first(), "pgh_created_at", None)
|
||||
|
||||
# Prepare data for serializer
|
||||
history_data = {
|
||||
"ride": ride,
|
||||
"current_state": ride,
|
||||
"summary": {
|
||||
"total_events": self.get_queryset().count(),
|
||||
"first_recorded": (
|
||||
history_events.last().pgh_created_at if history_events else None
|
||||
),
|
||||
"last_modified": (
|
||||
history_events.first().pgh_created_at if history_events else None
|
||||
),
|
||||
"first_recorded": first_recorded,
|
||||
"last_modified": last_modified,
|
||||
},
|
||||
"events": history_events,
|
||||
}
|
||||
@@ -395,6 +419,12 @@ class RideHistoryViewSet(ReadOnlyModelViewSet):
|
||||
responses={200: UnifiedHistoryTimelineSerializer},
|
||||
tags=["History"],
|
||||
),
|
||||
retrieve=extend_schema(
|
||||
summary="Get unified history timeline item",
|
||||
description="Retrieve a specific item from the unified history timeline.",
|
||||
responses={200: UnifiedHistoryTimelineSerializer},
|
||||
tags=["History"],
|
||||
),
|
||||
)
|
||||
class UnifiedHistoryViewSet(ReadOnlyModelViewSet):
|
||||
"""
|
||||
@@ -409,149 +439,54 @@ class UnifiedHistoryViewSet(ReadOnlyModelViewSet):
|
||||
ordering_fields = ["pgh_created_at"]
|
||||
ordering = ["-pgh_created_at"]
|
||||
|
||||
def get_queryset(self):
|
||||
def get_queryset(self): # type: ignore[override]
|
||||
"""Get unified history events across all tracked models."""
|
||||
queryset = (
|
||||
pghistory.models.Events.objects.filter(
|
||||
pgh_model__in=[
|
||||
"parks.park",
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
"companies.operator",
|
||||
"companies.propertyowner",
|
||||
"companies.manufacturer",
|
||||
"companies.designer",
|
||||
"accounts.user",
|
||||
]
|
||||
)
|
||||
pghistory.models.Events.objects.filter(pgh_model__in=ALL_TRACKED_MODELS)
|
||||
.select_related()
|
||||
.order_by("-pgh_created_at")
|
||||
)
|
||||
|
||||
# Apply filters
|
||||
model_type = self.request.query_params.get("model_type")
|
||||
if model_type:
|
||||
if model_type == "park":
|
||||
queryset = queryset.filter(pgh_model="parks.park")
|
||||
elif model_type == "ride":
|
||||
queryset = queryset.filter(
|
||||
pgh_model__in=[
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
]
|
||||
)
|
||||
elif model_type == "company":
|
||||
queryset = queryset.filter(
|
||||
pgh_model__in=[
|
||||
"companies.operator",
|
||||
"companies.propertyowner",
|
||||
"companies.manufacturer",
|
||||
"companies.designer",
|
||||
]
|
||||
)
|
||||
elif model_type == "user":
|
||||
queryset = queryset.filter(pgh_model="accounts.user")
|
||||
# Filter by requested model_type (if provided)
|
||||
model_type = cast(Request, self.request).query_params.get("model_type")
|
||||
if model_type == "park":
|
||||
queryset = queryset.filter(pgh_model=PARK_MODEL)
|
||||
elif model_type == "ride":
|
||||
queryset = queryset.filter(pgh_model__in=RIDE_MODELS)
|
||||
elif model_type == "company":
|
||||
queryset = queryset.filter(pgh_model__in=COMPANY_MODELS)
|
||||
elif model_type == "user":
|
||||
queryset = queryset.filter(pgh_model=ACCOUNT_MODEL)
|
||||
|
||||
# Filter by event type
|
||||
event_type = self.request.query_params.get("event_type")
|
||||
if event_type:
|
||||
if event_type == "created":
|
||||
queryset = queryset.filter(pgh_label="created")
|
||||
elif event_type == "updated":
|
||||
queryset = queryset.filter(pgh_label="updated")
|
||||
elif event_type == "deleted":
|
||||
queryset = queryset.filter(pgh_label="deleted")
|
||||
|
||||
# Filter by date range
|
||||
start_date = self.request.query_params.get("start_date")
|
||||
if start_date:
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
||||
start_datetime = datetime.strptime(start_date, "%Y-%m-%d")
|
||||
queryset = queryset.filter(pgh_created_at__gte=start_datetime)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
end_date = self.request.query_params.get("end_date")
|
||||
if end_date:
|
||||
try:
|
||||
from datetime import datetime
|
||||
|
||||
end_datetime = datetime.strptime(end_date, "%Y-%m-%d")
|
||||
queryset = queryset.filter(pgh_created_at__lte=end_datetime)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
# Apply limit
|
||||
limit = self.request.query_params.get("limit", "100")
|
||||
try:
|
||||
limit = min(int(limit), 1000) # Max 1000 events
|
||||
queryset = queryset[:limit]
|
||||
except (ValueError, TypeError):
|
||||
queryset = queryset[:100]
|
||||
# Apply shared list filters when serving the list action
|
||||
if self.action == "list":
|
||||
queryset = _apply_list_filters(
|
||||
queryset, cast(Request, self.request), default_limit=100, max_limit=1000
|
||||
)
|
||||
|
||||
return queryset
|
||||
|
||||
def get_serializer_class(self):
|
||||
def get_serializer_class(self): # type: ignore[override]
|
||||
"""Return unified history timeline serializer."""
|
||||
return UnifiedHistoryTimelineSerializer
|
||||
|
||||
def list(self, request):
|
||||
"""Get unified history timeline with summary statistics."""
|
||||
events = self.get_queryset()
|
||||
events = list(self.get_queryset()) # evaluate for counts / earliest/latest use
|
||||
|
||||
# Calculate summary statistics
|
||||
# Summary statistics across all tracked models
|
||||
total_events = pghistory.models.Events.objects.filter(
|
||||
pgh_model__in=[
|
||||
"parks.park",
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
"companies.operator",
|
||||
"companies.propertyowner",
|
||||
"companies.manufacturer",
|
||||
"companies.designer",
|
||||
"accounts.user",
|
||||
]
|
||||
pgh_model__in=ALL_TRACKED_MODELS
|
||||
).count()
|
||||
|
||||
# Get event type counts
|
||||
event_type_counts = (
|
||||
pghistory.models.Events.objects.filter(
|
||||
pgh_model__in=[
|
||||
"parks.park",
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
"companies.operator",
|
||||
"companies.propertyowner",
|
||||
"companies.manufacturer",
|
||||
"companies.designer",
|
||||
"accounts.user",
|
||||
]
|
||||
)
|
||||
pghistory.models.Events.objects.filter(pgh_model__in=ALL_TRACKED_MODELS)
|
||||
.values("pgh_label")
|
||||
.annotate(count=Count("id"))
|
||||
)
|
||||
|
||||
# Get model type counts
|
||||
model_type_counts = (
|
||||
pghistory.models.Events.objects.filter(
|
||||
pgh_model__in=[
|
||||
"parks.park",
|
||||
"rides.ride",
|
||||
"rides.ridemodel",
|
||||
"rides.rollercoasterstats",
|
||||
"companies.operator",
|
||||
"companies.propertyowner",
|
||||
"companies.manufacturer",
|
||||
"companies.designer",
|
||||
"accounts.user",
|
||||
]
|
||||
)
|
||||
pghistory.models.Events.objects.filter(pgh_model__in=ALL_TRACKED_MODELS)
|
||||
.values("pgh_model")
|
||||
.annotate(count=Count("id"))
|
||||
)
|
||||
@@ -567,8 +502,8 @@ class UnifiedHistoryViewSet(ReadOnlyModelViewSet):
|
||||
item["pgh_model"]: item["count"] for item in model_type_counts
|
||||
},
|
||||
"time_range": {
|
||||
"earliest": events.last().pgh_created_at if events else None,
|
||||
"latest": events.first().pgh_created_at if events else None,
|
||||
"earliest": events[-1].pgh_created_at if events else None,
|
||||
"latest": events[0].pgh_created_at if events else None,
|
||||
},
|
||||
},
|
||||
"events": events,
|
||||
|
||||
Reference in New Issue
Block a user