mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2026-02-05 11:05:17 -05:00
w
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user