mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 09:45:17 -05:00
lol
This commit is contained in:
@@ -12,19 +12,23 @@ from .company import Company
|
||||
from .credits import RideCredit
|
||||
from .location import RideLocation
|
||||
from .media import RidePhoto
|
||||
from .name_history import RideNameHistory
|
||||
from .rankings import RankingSnapshot, RidePairComparison, RideRanking
|
||||
from .reviews import RideReview
|
||||
from .rides import Ride, RideModel, RollerCoasterStats
|
||||
from .stats import DarkRideStats, FlatRideStats, WaterRideStats
|
||||
from .stats import DarkRideStats, FlatRideStats, KiddieRideStats, TransportationStats, WaterRideStats
|
||||
|
||||
__all__ = [
|
||||
# Primary models
|
||||
"Ride",
|
||||
"RideModel",
|
||||
"RideNameHistory",
|
||||
"RollerCoasterStats",
|
||||
"WaterRideStats",
|
||||
"DarkRideStats",
|
||||
"FlatRideStats",
|
||||
"KiddieRideStats",
|
||||
"TransportationStats",
|
||||
"Company",
|
||||
"RideLocation",
|
||||
"RideReview",
|
||||
@@ -35,3 +39,4 @@ __all__ = [
|
||||
"RidePairComparison",
|
||||
"RankingSnapshot",
|
||||
]
|
||||
|
||||
|
||||
73
backend/apps/rides/models/name_history.py
Normal file
73
backend/apps/rides/models/name_history.py
Normal file
@@ -0,0 +1,73 @@
|
||||
"""
|
||||
Ride Name History model for tracking historical ride names.
|
||||
|
||||
This model stores the history of name changes for rides, enabling display of
|
||||
former names on ride detail pages.
|
||||
"""
|
||||
|
||||
import pghistory
|
||||
from django.db import models
|
||||
|
||||
from apps.core.models import TrackedModel
|
||||
|
||||
|
||||
@pghistory.track()
|
||||
class RideNameHistory(TrackedModel):
|
||||
"""
|
||||
Tracks historical names of rides.
|
||||
|
||||
When a ride is renamed, this model stores the previous name along with
|
||||
the year range it was used and an optional reason for the change.
|
||||
"""
|
||||
|
||||
ride = models.ForeignKey(
|
||||
"rides.Ride",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="former_names",
|
||||
help_text="The ride this name history entry belongs to",
|
||||
)
|
||||
former_name = models.CharField(
|
||||
max_length=200,
|
||||
help_text="The previous name of the ride",
|
||||
)
|
||||
from_year = models.PositiveSmallIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Year the ride started using this name",
|
||||
)
|
||||
to_year = models.PositiveSmallIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Year the ride stopped using this name",
|
||||
)
|
||||
reason = models.CharField(
|
||||
max_length=500,
|
||||
blank=True,
|
||||
help_text="Reason for the name change (e.g., 'Retheme to Peanuts')",
|
||||
)
|
||||
|
||||
class Meta(TrackedModel.Meta):
|
||||
verbose_name = "Ride Name History"
|
||||
verbose_name_plural = "Ride Name Histories"
|
||||
ordering = ["-to_year", "-from_year"]
|
||||
indexes = [
|
||||
models.Index(fields=["ride", "-to_year"]),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
year_range = ""
|
||||
if self.from_year and self.to_year:
|
||||
year_range = f" ({self.from_year}-{self.to_year})"
|
||||
elif self.to_year:
|
||||
year_range = f" (until {self.to_year})"
|
||||
elif self.from_year:
|
||||
year_range = f" (from {self.from_year})"
|
||||
return f"{self.former_name}{year_range}"
|
||||
|
||||
def clean(self):
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
if self.from_year and self.to_year and self.from_year > self.to_year:
|
||||
raise ValidationError(
|
||||
{"from_year": "From year cannot be after to year."}
|
||||
)
|
||||
@@ -508,7 +508,21 @@ class Ride(StateMachineMixin, TrackedModel):
|
||||
help_text="Status to change to after closing date",
|
||||
)
|
||||
opening_date = models.DateField(null=True, blank=True)
|
||||
opening_date_precision = models.CharField(
|
||||
max_length=10,
|
||||
choices=[("YEAR", "Year"), ("MONTH", "Month"), ("DAY", "Day")],
|
||||
default="DAY",
|
||||
blank=True,
|
||||
help_text="Precision of the opening date",
|
||||
)
|
||||
closing_date = models.DateField(null=True, blank=True)
|
||||
closing_date_precision = models.CharField(
|
||||
max_length=10,
|
||||
choices=[("YEAR", "Year"), ("MONTH", "Month"), ("DAY", "Day")],
|
||||
default="DAY",
|
||||
blank=True,
|
||||
help_text="Precision of the closing date",
|
||||
)
|
||||
status_since = models.DateField(null=True, blank=True)
|
||||
min_height_in = models.PositiveIntegerField(null=True, blank=True)
|
||||
max_height_in = models.PositiveIntegerField(null=True, blank=True)
|
||||
@@ -516,6 +530,18 @@ class Ride(StateMachineMixin, TrackedModel):
|
||||
ride_duration_seconds = models.PositiveIntegerField(null=True, blank=True)
|
||||
average_rating = models.DecimalField(max_digits=3, decimal_places=2, null=True, blank=True)
|
||||
|
||||
# Additional ride classification
|
||||
ride_sub_type = models.CharField(
|
||||
max_length=100,
|
||||
blank=True,
|
||||
help_text="Sub-category of ride (e.g., 'Flying Coaster', 'Inverted Coaster', 'Log Flume')",
|
||||
)
|
||||
age_requirement = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Minimum age requirement in years (if any)",
|
||||
)
|
||||
|
||||
# Computed fields for hybrid filtering
|
||||
opening_year = models.IntegerField(null=True, blank=True, db_index=True)
|
||||
search_text = models.TextField(blank=True, db_index=True)
|
||||
@@ -680,6 +706,14 @@ class Ride(StateMachineMixin, TrackedModel):
|
||||
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def is_closing(self) -> bool:
|
||||
"""Returns True if this ride has a closing date in the future (announced closure)."""
|
||||
from django.utils import timezone
|
||||
if self.closing_date:
|
||||
return self.closing_date > timezone.now().date()
|
||||
return False
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
# Handle slug generation and conflicts
|
||||
if not self.slug:
|
||||
|
||||
@@ -224,3 +224,152 @@ class FlatRideStats(TrackedModel):
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Flat Ride Stats for {self.ride.name}"
|
||||
|
||||
|
||||
# Transport Type Choices for Transportation Rides
|
||||
TRANSPORT_TYPES = [
|
||||
("TRAIN", "Train"),
|
||||
("MONORAIL", "Monorail"),
|
||||
("SKYLIFT", "Skylift / Chairlift"),
|
||||
("FERRY", "Ferry / Boat"),
|
||||
("PEOPLEMOVER", "PeopleMover"),
|
||||
("CABLE_CAR", "Cable Car"),
|
||||
("TRAM", "Tram"),
|
||||
]
|
||||
|
||||
|
||||
@pghistory.track()
|
||||
class KiddieRideStats(TrackedModel):
|
||||
"""
|
||||
Statistics specific to kiddie rides (category=KR).
|
||||
|
||||
Tracks age-appropriate ride characteristics and theming.
|
||||
"""
|
||||
|
||||
ride = models.OneToOneField(
|
||||
"rides.Ride",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="kiddie_stats",
|
||||
help_text="Ride these kiddie ride statistics belong to",
|
||||
)
|
||||
|
||||
min_age = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Minimum recommended age in years",
|
||||
)
|
||||
|
||||
max_age = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Maximum recommended age in years",
|
||||
)
|
||||
|
||||
educational_theme = models.CharField(
|
||||
max_length=200,
|
||||
blank=True,
|
||||
help_text="Educational theme if applicable (e.g., 'Dinosaurs', 'Space')",
|
||||
)
|
||||
|
||||
character_theme = models.CharField(
|
||||
max_length=200,
|
||||
blank=True,
|
||||
help_text="Character theme if applicable (e.g., 'Paw Patrol', 'Peppa Pig')",
|
||||
)
|
||||
|
||||
guardian_required = models.BooleanField(
|
||||
default=False,
|
||||
help_text="Whether a guardian must be present during the ride",
|
||||
)
|
||||
|
||||
adult_ride_along = models.BooleanField(
|
||||
default=True,
|
||||
help_text="Whether adults can ride along with children",
|
||||
)
|
||||
|
||||
seats_per_vehicle = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Number of seats per ride vehicle",
|
||||
)
|
||||
|
||||
class Meta(TrackedModel.Meta):
|
||||
verbose_name = "Kiddie Ride Statistics"
|
||||
verbose_name_plural = "Kiddie Ride Statistics"
|
||||
ordering = ["ride"]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Kiddie Ride Stats for {self.ride.name}"
|
||||
|
||||
|
||||
@pghistory.track()
|
||||
class TransportationStats(TrackedModel):
|
||||
"""
|
||||
Statistics specific to transportation rides (category=TR).
|
||||
|
||||
Tracks route, capacity, and vehicle information.
|
||||
"""
|
||||
|
||||
ride = models.OneToOneField(
|
||||
"rides.Ride",
|
||||
on_delete=models.CASCADE,
|
||||
related_name="transport_stats",
|
||||
help_text="Ride these transportation statistics belong to",
|
||||
)
|
||||
|
||||
transport_type = models.CharField(
|
||||
max_length=20,
|
||||
choices=TRANSPORT_TYPES,
|
||||
default="TRAIN",
|
||||
help_text="Type of transportation",
|
||||
)
|
||||
|
||||
route_length_ft = models.DecimalField(
|
||||
max_digits=8,
|
||||
decimal_places=2,
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Total route length in feet",
|
||||
)
|
||||
|
||||
stations_count = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Number of stations/stops on the route",
|
||||
)
|
||||
|
||||
vehicle_capacity = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Passenger capacity per vehicle",
|
||||
)
|
||||
|
||||
vehicles_count = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Total number of vehicles in operation",
|
||||
)
|
||||
|
||||
round_trip_duration_minutes = models.PositiveIntegerField(
|
||||
null=True,
|
||||
blank=True,
|
||||
help_text="Duration of a complete round trip in minutes",
|
||||
)
|
||||
|
||||
scenic_highlights = models.TextField(
|
||||
blank=True,
|
||||
help_text="Notable scenic views or attractions along the route",
|
||||
)
|
||||
|
||||
is_one_way = models.BooleanField(
|
||||
default=False,
|
||||
help_text="Whether this is a one-way transportation (vs round-trip)",
|
||||
)
|
||||
|
||||
class Meta(TrackedModel.Meta):
|
||||
verbose_name = "Transportation Statistics"
|
||||
verbose_name_plural = "Transportation Statistics"
|
||||
ordering = ["ride"]
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"Transportation Stats for {self.ride.name}"
|
||||
|
||||
Reference in New Issue
Block a user