This commit is contained in:
pacnpal
2026-01-08 13:44:37 -05:00
parent 40cba5bdb2
commit fe960e8b62
21 changed files with 2008 additions and 59 deletions

View File

@@ -22,9 +22,70 @@ class Company(TrackedModel):
)
description = models.TextField(blank=True, help_text="Detailed company description")
website = models.URLField(blank=True, help_text="Company website URL")
# Person/Entity type
PERSON_TYPE_CHOICES = [
("company", "Company"),
("individual", "Individual"),
("firm", "Firm"),
("organization", "Organization"),
]
person_type = models.CharField(
max_length=20,
choices=PERSON_TYPE_CHOICES,
blank=True,
default="company",
help_text="Type of entity (company, individual, firm, organization)",
)
# General company info
founded_date = models.DateField(null=True, blank=True, help_text="Date the company was founded")
founded_date_precision = models.CharField(
max_length=20,
choices=[
("exact", "Exact"),
("month", "Month"),
("year", "Year"),
("decade", "Decade"),
("century", "Century"),
("approximate", "Approximate"),
],
blank=True,
default="",
help_text="Precision of the founded date",
)
founded_year = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Year the company was founded (alternative to founded_date)",
)
headquarters_location = models.CharField(
max_length=200,
blank=True,
help_text="Headquarters location description (e.g., 'Los Angeles, CA, USA')",
)
# Location relationship (optional)
location = models.ForeignKey(
"parks.ParkLocation",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="companies",
help_text="Linked location record for headquarters",
)
# Image settings - stored as Cloudflare image IDs/URLs
banner_image_id = models.CharField(
max_length=255,
blank=True,
help_text="Cloudflare image ID for banner image",
)
card_image_id = models.CharField(
max_length=255,
blank=True,
help_text="Cloudflare image ID for card image",
)
# Manufacturer-specific fields
rides_count = models.IntegerField(default=0, help_text="Number of rides manufactured (auto-calculated)")
@@ -33,6 +94,16 @@ class Company(TrackedModel):
# Frontend URL
url = models.URLField(blank=True, help_text="Frontend URL for this company")
# Submission metadata fields (from frontend schema)
source_url = models.URLField(
blank=True,
help_text="Source URL for the data (e.g., official website, Wikipedia)",
)
is_test_data = models.BooleanField(
default=False,
help_text="Whether this is test/development data",
)
def __str__(self):
return self.name

View File

@@ -2,6 +2,7 @@ import contextlib
import pghistory
from django.contrib.auth.models import AbstractBaseUser
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.text import slugify
@@ -44,6 +45,11 @@ class RideModel(TrackedModel):
blank=True,
help_text="Primary category classification",
)
ride_type = models.CharField(
max_length=100,
blank=True,
help_text="Specific ride type within the category (e.g., 'Flying Coaster', 'Inverted Coaster')",
)
# Technical specifications
typical_height_range_min_ft = models.DecimalField(
@@ -155,6 +161,16 @@ class RideModel(TrackedModel):
# Frontend URL
url = models.URLField(blank=True, help_text="Frontend URL for this ride model")
# Submission metadata fields (from frontend schema)
source_url = models.URLField(
blank=True,
help_text="Source URL for the data (e.g., manufacturer website)",
)
is_test_data = models.BooleanField(
default=False,
help_text="Whether this is test/development data",
)
class Meta(TrackedModel.Meta):
verbose_name = "Ride Model"
verbose_name_plural = "Ride Models"
@@ -509,17 +525,31 @@ class Ride(StateMachineMixin, TrackedModel):
)
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",
max_length=20,
choices=[
("exact", "Exact Date"),
("month", "Month and Year"),
("year", "Year Only"),
("decade", "Decade"),
("century", "Century"),
("approximate", "Approximate"),
],
default="exact",
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",
max_length=20,
choices=[
("exact", "Exact Date"),
("month", "Month and Year"),
("year", "Year Only"),
("decade", "Decade"),
("century", "Century"),
("approximate", "Approximate"),
],
default="exact",
blank=True,
help_text="Precision of the closing date",
)
@@ -541,11 +571,268 @@ class Ride(StateMachineMixin, TrackedModel):
blank=True,
help_text="Minimum age requirement in years (if any)",
)
height_requirement_cm = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Minimum height requirement in centimeters",
)
duration_seconds = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Ride duration in seconds",
)
inversions_count = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of inversions (for coasters)",
)
# 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)
# ===== CATEGORY-SPECIFIC FIELDS =====
# These fields support the frontend validation schemas in entityValidationSchemas.ts
# Fields are nullable since they only apply to specific ride categories
# --- Core Stats (6 fields) ---
max_speed_kmh = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True,
help_text="Maximum speed in kilometers per hour",
)
height_meters = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True,
help_text="Height of the ride structure in meters",
)
length_meters = models.DecimalField(
max_digits=8,
decimal_places=2,
null=True,
blank=True,
help_text="Total track/ride length in meters",
)
drop_meters = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True,
help_text="Maximum drop height in meters",
)
gforce_max = models.DecimalField(
max_digits=4,
decimal_places=2,
null=True,
blank=True,
help_text="Maximum G-force experienced",
)
intensity_level = models.CharField(
max_length=20,
blank=True,
help_text="Intensity classification: family, thrill, or extreme",
)
# --- Coaster-Specific (5 fields) ---
coaster_type = models.CharField(
max_length=20,
blank=True,
help_text="Coaster structure type: steel, wood, or hybrid",
)
seating_type = models.CharField(
max_length=20,
blank=True,
help_text="Seating configuration: sit_down, inverted, flying, stand_up, etc.",
)
track_material = ArrayField(
models.CharField(max_length=50),
blank=True,
default=list,
help_text="Track material types (e.g., ['steel', 'wood'])",
)
support_material = ArrayField(
models.CharField(max_length=50),
blank=True,
default=list,
help_text="Support structure material types",
)
propulsion_method = ArrayField(
models.CharField(max_length=50),
blank=True,
default=list,
help_text="Propulsion methods (e.g., ['chain_lift', 'lsm'])",
)
# --- Water Ride (5 fields) ---
water_depth_cm = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Water depth in centimeters",
)
splash_height_meters = models.DecimalField(
max_digits=5,
decimal_places=2,
null=True,
blank=True,
help_text="Maximum splash height in meters",
)
wetness_level = models.CharField(
max_length=20,
blank=True,
help_text="Expected wetness: dry, light, moderate, or soaked",
)
flume_type = models.CharField(
max_length=100,
blank=True,
help_text="Type of flume or water channel",
)
boat_capacity = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of passengers per boat/vehicle",
)
# --- Dark Ride (7 fields) ---
theme_name = models.CharField(
max_length=200,
blank=True,
help_text="Primary theme or IP name",
)
story_description = models.TextField(
blank=True,
help_text="Narrative or story description for the ride",
)
show_duration_seconds = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Duration of show elements in seconds",
)
animatronics_count = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of animatronic figures",
)
projection_type = models.CharField(
max_length=100,
blank=True,
help_text="Type of projection technology used",
)
ride_system = models.CharField(
max_length=100,
blank=True,
help_text="Ride system type (e.g., trackless, omnimover)",
)
scenes_count = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of distinct scenes or show sections",
)
# --- Flat Ride (7 fields) ---
rotation_type = models.CharField(
max_length=20,
blank=True,
help_text="Rotation axis: horizontal, vertical, multi_axis, pendulum, or none",
)
motion_pattern = models.CharField(
max_length=200,
blank=True,
help_text="Description of the ride's motion pattern",
)
platform_count = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of ride platforms or gondolas",
)
swing_angle_degrees = models.DecimalField(
max_digits=5,
decimal_places=2,
null=True,
blank=True,
help_text="Maximum swing angle in degrees",
)
rotation_speed_rpm = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True,
help_text="Rotation speed in revolutions per minute",
)
arm_length_meters = models.DecimalField(
max_digits=5,
decimal_places=2,
null=True,
blank=True,
help_text="Length of ride arm in meters",
)
max_height_reached_meters = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True,
help_text="Maximum height reached during ride cycle in meters",
)
# --- Kiddie Ride (4 fields) ---
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 or learning theme if applicable",
)
character_theme = models.CharField(
max_length=200,
blank=True,
help_text="Character or IP theme (e.g., Paw Patrol, Sesame Street)",
)
# --- Transportation (6 fields) ---
transport_type = models.CharField(
max_length=20,
blank=True,
help_text="Transport mode: train, monorail, skylift, ferry, peoplemover, or cable_car",
)
route_length_meters = models.DecimalField(
max_digits=8,
decimal_places=2,
null=True,
blank=True,
help_text="Total route length in meters",
)
stations_count = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of stations or stops",
)
vehicle_capacity = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Passenger capacity per vehicle",
)
vehicles_count = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Number of vehicles in operation",
)
round_trip_duration_seconds = models.PositiveIntegerField(
null=True,
blank=True,
help_text="Duration of a complete round trip in seconds",
)
# Image settings - references to existing photos
banner_image = models.ForeignKey(
"RidePhoto",
@@ -568,6 +855,16 @@ class Ride(StateMachineMixin, TrackedModel):
url = models.URLField(blank=True, help_text="Frontend URL for this ride")
park_url = models.URLField(blank=True, help_text="Frontend URL for this ride's park")
# Submission metadata fields (from frontend schema)
source_url = models.URLField(
blank=True,
help_text="Source URL for the data (e.g., official website, RCDB)",
)
is_test_data = models.BooleanField(
default=False,
help_text="Whether this is test/development data",
)
class Meta(TrackedModel.Meta):
verbose_name = "Ride"
verbose_name_plural = "Rides"