feat: Implement MFA authentication, add ride statistics model, and update various services, APIs, and tests across the application.

This commit is contained in:
pacnpal
2025-12-28 17:32:53 -05:00
parent aa56c46c27
commit c95f99ca10
452 changed files with 7948 additions and 6073 deletions

View File

@@ -1,20 +1,23 @@
from typing import TYPE_CHECKING, Any, Optional
import pghistory
from django.core.exceptions import ValidationError
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from django.core.exceptions import ValidationError
from config.django import base as settings
from typing import Optional, Any, TYPE_CHECKING, List
import pghistory
from apps.core.history import TrackedModel
from apps.core.choices import RichChoiceField
from apps.core.state_machine import RichFSMField, StateMachineMixin
from apps.core.choices import RichChoiceField
from apps.core.history import TrackedModel
from apps.core.state_machine import RichFSMField, StateMachineMixin
from config.django import base as settings
if TYPE_CHECKING:
from apps.rides.models import Ride
from . import ParkArea
from django.contrib.auth.models import AbstractBaseUser
from apps.rides.models import Ride
from . import ParkArea
@pghistory.track()
class Park(StateMachineMixin, TrackedModel):
@@ -57,6 +60,8 @@ class Park(StateMachineMixin, TrackedModel):
max_digits=10, decimal_places=2, null=True, blank=True, help_text="Park size in acres"
)
website = models.URLField(blank=True, help_text="Official website URL")
phone = models.CharField(max_length=30, blank=True, help_text="Contact phone number")
email = models.EmailField(blank=True, help_text="Contact email address")
# Statistics
average_rating = models.DecimalField(
@@ -113,17 +118,17 @@ class Park(StateMachineMixin, TrackedModel):
# Computed fields for hybrid filtering
opening_year = models.IntegerField(
null=True,
blank=True,
null=True,
blank=True,
db_index=True,
help_text="Year the park opened (computed from opening_date)"
)
search_text = models.TextField(
blank=True,
blank=True,
db_index=True,
help_text="Searchable text combining name, description, location, and operator"
)
# Timezone for park operations
timezone = models.CharField(
max_length=50,
@@ -220,6 +225,7 @@ class Park(StateMachineMixin, TrackedModel):
def save(self, *args: Any, **kwargs: Any) -> None:
from django.contrib.contenttypes.models import ContentType
from apps.core.history import HistoricalSlug
# Get old instance if it exists
@@ -264,13 +270,13 @@ class Park(StateMachineMixin, TrackedModel):
self.opening_year = self.opening_date.year
else:
self.opening_year = None
# Populate search_text for client-side filtering
search_parts = [self.name]
if self.description:
search_parts.append(self.description)
# Add location information if available
try:
if hasattr(self, 'location') and self.location:
@@ -283,15 +289,15 @@ class Park(StateMachineMixin, TrackedModel):
except Exception:
# Handle case where location relationship doesn't exist yet
pass
# Add operator information
if self.operator:
search_parts.append(self.operator.name)
# Add property owner information if different
if self.property_owner and self.property_owner != self.operator:
search_parts.append(self.property_owner.name)
# Combine all parts into searchable text
self.search_text = ' '.join(filter(None, search_parts)).lower()
@@ -315,7 +321,7 @@ class Park(StateMachineMixin, TrackedModel):
return ""
@property
def coordinates(self) -> Optional[List[float]]:
def coordinates(self) -> list[float] | None:
"""Returns coordinates as a list [latitude, longitude]"""
if hasattr(self, "location") and self.location:
coords = self.location.coordinates
@@ -327,6 +333,7 @@ class Park(StateMachineMixin, TrackedModel):
def get_by_slug(cls, slug: str) -> tuple["Park", bool]:
"""Get park by current or historical slug"""
from django.contrib.contenttypes.models import ContentType
from apps.core.history import HistoricalSlug
print(f"\nLooking up slug: {slug}")