major changes, including tailwind v4

This commit is contained in:
pacnpal
2025-08-15 12:24:20 -04:00
parent f6c8e0e25c
commit da7c7e3381
261 changed files with 22783 additions and 10465 deletions

View File

@@ -1,168 +1,73 @@
from django.contrib import admin
from django.utils.html import format_html
from django.db.models import Avg
from .models import Ride, RollerCoasterStats
from django.contrib.gis.admin import GISModelAdmin
from .models.company import Company
from .models.rides import Ride
from .models.location import RideLocation
class RollerCoasterStatsInline(admin.StackedInline):
model = RollerCoasterStats
can_delete = False
class ManufacturerAdmin(admin.ModelAdmin):
list_display = ('name', 'headquarters', 'website', 'rides_count')
search_fields = ('name',)
def get_queryset(self, request):
return super().get_queryset(request).filter(roles__contains=['MANUFACTURER'])
class DesignerAdmin(admin.ModelAdmin):
list_display = ('name', 'headquarters', 'website')
search_fields = ('name',)
def get_queryset(self, request):
return super().get_queryset(request).filter(roles__contains=['DESIGNER'])
class RideLocationInline(admin.StackedInline):
"""Inline admin for RideLocation"""
model = RideLocation
extra = 0
fieldsets = (
('Basic Stats', {
'fields': (
('height_ft', 'length_ft'),
('speed_mph', 'inversions'),
'ride_time_seconds'
)
}),
('Track Details', {
'fields': (
'track_type',
'launch_type'
)
}),
('Train Configuration', {
'fields': (
'train_style',
('trains_count', 'cars_per_train', 'seats_per_car')
)
}),
fields = (
'park_area',
'point',
'entrance_notes',
'accessibility_notes',
)
@admin.register(Ride)
class RideAdmin(admin.ModelAdmin):
list_display = ('id', 'name', 'park', 'category', 'get_status', 'manufacturer', 'opening_date', 'get_avg_rating')
list_filter = ('status', 'category', 'manufacturer', 'park')
search_fields = ('name', 'park__name', 'manufacturer__name', 'description')
prepopulated_fields = {'slug': ('name',)}
inlines = [RollerCoasterStatsInline]
readonly_fields = ('id', 'created_at', 'updated_at')
actions = ['mark_as_operating', 'mark_as_closed', 'mark_as_under_maintenance', 'mark_as_removed']
class RideLocationAdmin(GISModelAdmin):
"""Admin for standalone RideLocation management"""
list_display = ('ride', 'park_area', 'has_coordinates', 'created_at')
list_filter = ('park_area', 'created_at')
search_fields = ('ride__name', 'park_area', 'entrance_notes')
readonly_fields = ('latitude', 'longitude', 'coordinates', 'created_at', 'updated_at')
fieldsets = (
('Basic Information', {
'fields': (
'name',
'slug',
'description',
'park',
'park_area'
)
('Ride', {
'fields': ('ride',)
}),
('Ride Details', {
'fields': (
'category',
'manufacturer',
'model_name',
'status'
)
('Location Information', {
'fields': ('park_area', 'point', 'latitude', 'longitude', 'coordinates'),
'description': 'Optional coordinates - not all rides need precise location tracking'
}),
('Dates', {
'fields': (
'opening_date',
'closing_date',
'status_since'
)
}),
('Requirements', {
'fields': (
'min_height_in',
'max_height_in',
'accessibility_options'
)
}),
('Capacity', {
'fields': (
'capacity_per_hour',
'ride_duration_seconds'
)
('Navigation Notes', {
'fields': ('entrance_notes', 'accessibility_notes'),
}),
('Metadata', {
'fields': ('id', 'created_at', 'updated_at'),
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def get_status(self, obj):
status_colors = {
'OPERATING': 'green',
'CLOSED_TEMP': 'orange',
'CLOSED_PERM': 'red',
'UNDER_CONSTRUCTION': 'blue',
'DEMOLISHED': 'grey',
'RELOCATED': 'purple'
}
return format_html(
'<span style="color: {};">{}</span>',
status_colors.get(obj.status, 'black'),
obj.get_status_display()
)
get_status.short_description = 'Status'
def latitude(self, obj):
return obj.latitude
latitude.short_description = 'Latitude'
def get_avg_rating(self, obj):
avg = obj.reviews.filter(is_published=True).aggregate(avg_rating=Avg('rating'))['avg_rating']
if avg:
rating_str = '{:.1f}'.format(float(avg))
return format_html(
'<span style="color: gold;">★ {}</span>',
rating_str
)
return '-'
get_avg_rating.short_description = 'Rating'
def longitude(self, obj):
return obj.longitude
longitude.short_description = 'Longitude'
def mark_as_operating(self, request, queryset):
queryset.update(status='OPERATING')
mark_as_operating.short_description = "Mark selected rides as operating"
def mark_as_closed(self, request, queryset):
queryset.update(status='CLOSED_TEMP')
mark_as_closed.short_description = "Mark selected rides as temporarily closed"
class RideAdmin(admin.ModelAdmin):
"""Enhanced Ride admin with location inline"""
inlines = [RideLocationInline]
def mark_as_under_maintenance(self, request, queryset):
queryset.update(status='CLOSED_TEMP')
mark_as_under_maintenance.short_description = "Mark selected rides as under maintenance"
def mark_as_removed(self, request, queryset):
queryset.update(status='DEMOLISHED')
mark_as_removed.short_description = "Mark selected rides as demolished"
@admin.register(RollerCoasterStats)
class RollerCoasterStatsAdmin(admin.ModelAdmin):
list_display = ('ride', 'height_ft', 'length_ft', 'speed_mph', 'inversions', 'get_capacity')
list_filter = ('launch_type', 'track_type', 'train_style')
search_fields = ('ride__name', 'track_type')
readonly_fields = ('id', 'ride')
fieldsets = (
('Basic Stats', {
'fields': (
'id',
'ride',
('height_ft', 'length_ft'),
('speed_mph', 'inversions'),
'ride_time_seconds'
)
}),
('Track Details', {
'fields': (
'track_type',
'launch_type'
)
}),
('Train Configuration', {
'fields': (
'train_style',
('trains_count', 'cars_per_train', 'seats_per_car')
)
}),
)
def get_capacity(self, obj):
if obj.trains_count and obj.cars_per_train and obj.seats_per_car:
capacity = obj.trains_count * obj.cars_per_train * obj.seats_per_car
return format_html(
'{} seats total',
str(capacity)
)
return '-'
get_capacity.short_description = 'Total Capacity'
admin.site.register(Company)
admin.site.register(Ride, RideAdmin)
admin.site.register(RideLocation, RideLocationAdmin)

View File

@@ -1,10 +1,12 @@
from django import forms
from django.forms import ModelChoiceField
from django.urls import reverse_lazy
from .models import Ride, RideModel
from .models.company import Company
from .models.rides import Ride, RideModel
Manufacturer = Company
Designer = Company
from parks.models import Park, ParkArea
from manufacturers.models import Manufacturer
from designers.models import Designer
class RideForm(forms.ModelForm):
@@ -31,7 +33,7 @@ class RideForm(forms.ModelForm):
attrs={
"class": "w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white",
"placeholder": "Search for a manufacturer...",
"hx-get": reverse_lazy("rides:search_manufacturers"),
"hx-get": reverse_lazy("rides:search_companies"),
"hx-trigger": "click, input delay:200ms",
"hx-target": "#manufacturer-search-results",
"name": "q",
@@ -47,7 +49,7 @@ class RideForm(forms.ModelForm):
attrs={
"class": "w-full border-gray-300 rounded-lg form-input dark:border-gray-600 dark:bg-gray-700 dark:text-white",
"placeholder": "Search for a designer...",
"hx-get": reverse_lazy("rides:search_designers"),
"hx-get": reverse_lazy("rides:search_companies"),
"hx-trigger": "click, input delay:200ms",
"hx-target": "#designer-search-results",
"name": "q",

View File

@@ -1,22 +1,123 @@
# Generated by Django 5.1.4 on 2025-02-10 09:31
# Generated by Django 5.1.4 on 2025-08-13 21:35
import django.contrib.postgres.fields
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("manufacturers", "0001_initial"),
("designers", "0001_initial"),
("parks", "0001_initial"), # Changed to depend on initial parks migration
("parks", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="Company",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(max_length=255)),
("slug", models.SlugField(max_length=255, unique=True)),
(
"roles",
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
("MANUFACTURER", "Ride Manufacturer"),
("DESIGNER", "Ride Designer"),
],
max_length=20,
),
blank=True,
default=list,
size=None,
),
),
("description", models.TextField(blank=True)),
("website", models.URLField(blank=True)),
("rides_count", models.IntegerField(default=0)),
],
options={
"verbose_name_plural": "Companies",
"ordering": ["name"],
},
),
migrations.CreateModel(
name="RideModel",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(max_length=255)),
("description", models.TextField(blank=True)),
(
"category",
models.CharField(
blank=True,
choices=[
("", "Select ride type"),
("RC", "Roller Coaster"),
("DR", "Dark Ride"),
("FR", "Flat Ride"),
("WR", "Water Ride"),
("TR", "Transport"),
("OT", "Other"),
],
default="",
max_length=2,
),
),
(
"manufacturer",
models.ForeignKey(
blank=True,
limit_choices_to={"roles__contains": ["MANUFACTURER"]},
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="ride_models",
to="rides.company",
),
),
],
options={
"ordering": ["manufacturer", "name"],
"unique_together": {("manufacturer", "name")},
},
),
migrations.CreateModel(
name="Ride",
fields=[
("id", models.BigAutoField(primary_key=True, serialize=False)),
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(max_length=255)),
("slug", models.SlugField(max_length=255)),
("description", models.TextField(blank=True)),
@@ -41,7 +142,9 @@ class Migration(migrations.Migration):
"status",
models.CharField(
choices=[
("", "Select status"),
("OPERATING", "Operating"),
("CLOSED_TEMP", "Temporarily Closed"),
("SBNO", "Standing But Not Operating"),
("CLOSING", "Closing"),
("CLOSED_PERM", "Permanently Closed"),
@@ -71,33 +174,40 @@ class Migration(migrations.Migration):
("status_since", models.DateField(blank=True, null=True)),
("min_height_in", models.PositiveIntegerField(blank=True, null=True)),
("max_height_in", models.PositiveIntegerField(blank=True, null=True)),
("capacity_per_hour", models.PositiveIntegerField(blank=True, null=True)),
("ride_duration_seconds", models.PositiveIntegerField(blank=True, null=True)),
(
"capacity_per_hour",
models.PositiveIntegerField(blank=True, null=True),
),
(
"ride_duration_seconds",
models.PositiveIntegerField(blank=True, null=True),
),
(
"average_rating",
models.DecimalField(
blank=True, decimal_places=2, max_digits=3, null=True
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"designer",
models.ForeignKey(
blank=True,
limit_choices_to={"roles__contains": ["DESIGNER"]},
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="rides",
to="designers.designer",
related_name="designed_rides",
to="rides.company",
),
),
(
"manufacturer",
models.ForeignKey(
blank=True,
limit_choices_to={"roles__contains": ["MANUFACTURER"]},
null=True,
on_delete=django.db.models.deletion.CASCADE,
to="manufacturers.manufacturer",
on_delete=django.db.models.deletion.SET_NULL,
related_name="manufactured_rides",
to="rides.company",
),
),
(
@@ -118,9 +228,129 @@ class Migration(migrations.Migration):
to="parks.parkarea",
),
),
(
"ride_model",
models.ForeignKey(
blank=True,
help_text="The specific model/type of this ride",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="rides",
to="rides.ridemodel",
),
),
],
options={
"ordering": ["name"],
"unique_together": {("park", "slug")},
},
),
migrations.CreateModel(
name="RollerCoasterStats",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
),
(
"length_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=7, null=True
),
),
(
"speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
),
("inversions", models.PositiveIntegerField(default=0)),
(
"ride_time_seconds",
models.PositiveIntegerField(blank=True, null=True),
),
("track_type", models.CharField(blank=True, max_length=255)),
(
"track_material",
models.CharField(
blank=True,
choices=[
("STEEL", "Steel"),
("WOOD", "Wood"),
("HYBRID", "Hybrid"),
],
default="STEEL",
max_length=20,
),
),
(
"roller_coaster_type",
models.CharField(
blank=True,
choices=[
("SITDOWN", "Sit Down"),
("INVERTED", "Inverted"),
("FLYING", "Flying"),
("STANDUP", "Stand Up"),
("WING", "Wing"),
("DIVE", "Dive"),
("FAMILY", "Family"),
("WILD_MOUSE", "Wild Mouse"),
("SPINNING", "Spinning"),
("FOURTH_DIMENSION", "4th Dimension"),
("OTHER", "Other"),
],
default="SITDOWN",
max_length=20,
),
),
(
"max_drop_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
),
(
"launch_type",
models.CharField(
choices=[
("CHAIN", "Chain Lift"),
("LSM", "LSM Launch"),
("HYDRAULIC", "Hydraulic Launch"),
("GRAVITY", "Gravity"),
("OTHER", "Other"),
],
default="CHAIN",
max_length=20,
),
),
("train_style", models.CharField(blank=True, max_length=255)),
("trains_count", models.PositiveIntegerField(blank=True, null=True)),
("cars_per_train", models.PositiveIntegerField(blank=True, null=True)),
("seats_per_car", models.PositiveIntegerField(blank=True, null=True)),
(
"ride",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="coaster_stats",
to="rides.ride",
),
),
],
options={
"verbose_name": "Roller Coaster Statistics",
"verbose_name_plural": "Roller Coaster Statistics",
},
),
]

View File

@@ -1,66 +0,0 @@
# Generated by Django 5.1.4 on 2025-02-10 09:31
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("manufacturers", "0001_initial"),
("rides", "0001_initial"),
]
operations = [
migrations.CreateModel(
name="RideModel",
fields=[
("id", models.BigAutoField(primary_key=True, serialize=False)),
("name", models.CharField(max_length=255)),
("description", models.TextField(blank=True)),
(
"category",
models.CharField(
blank=True,
choices=[
("", "Select ride type"),
("RC", "Roller Coaster"),
("DR", "Dark Ride"),
("FR", "Flat Ride"),
("WR", "Water Ride"),
("TR", "Transport"),
("OT", "Other"),
],
default="",
max_length=2,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"manufacturer",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="ride_models",
to="manufacturers.manufacturer",
),
),
],
options={
"ordering": ["manufacturer", "name"],
"unique_together": {("manufacturer", "name")},
},
),
migrations.AddField(
model_name="ride",
name="ride_model",
field=models.ForeignKey(
blank=True,
help_text="The specific model/type of this ride",
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="rides",
to="rides.ridemodel",
),
),
]

View File

@@ -0,0 +1,190 @@
# Generated by Django 5.1.4 on 2025-08-14 14:50
import django.core.validators
import django.db.models.deletion
import pgtrigger.compiler
import pgtrigger.migrations
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pghistory", "0006_delete_aggregateevent"),
("rides", "0001_initial"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name="RideReview",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"rating",
models.PositiveSmallIntegerField(
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(10),
]
),
),
("title", models.CharField(max_length=200)),
("content", models.TextField()),
("visit_date", models.DateField()),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_published", models.BooleanField(default=True)),
("moderation_notes", models.TextField(blank=True)),
("moderated_at", models.DateTimeField(blank=True, null=True)),
(
"moderated_by",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="moderated_ride_reviews",
to=settings.AUTH_USER_MODEL,
),
),
(
"ride",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="reviews",
to="rides.ride",
),
),
(
"user",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="ride_reviews",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"ordering": ["-created_at"],
"unique_together": {("ride", "user")},
},
),
migrations.CreateModel(
name="RideReviewEvent",
fields=[
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
("pgh_label", models.TextField(help_text="The event label.")),
("id", models.BigIntegerField()),
(
"rating",
models.PositiveSmallIntegerField(
validators=[
django.core.validators.MinValueValidator(1),
django.core.validators.MaxValueValidator(10),
]
),
),
("title", models.CharField(max_length=200)),
("content", models.TextField()),
("visit_date", models.DateField()),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("is_published", models.BooleanField(default=True)),
("moderation_notes", models.TextField(blank=True)),
("moderated_at", models.DateTimeField(blank=True, null=True)),
(
"moderated_by",
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to=settings.AUTH_USER_MODEL,
),
),
(
"pgh_context",
models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="pghistory.context",
),
),
(
"pgh_obj",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="events",
to="rides.ridereview",
),
),
(
"ride",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="rides.ride",
),
),
(
"user",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to=settings.AUTH_USER_MODEL,
),
),
],
options={
"abstract": False,
},
),
pgtrigger.migrations.AddTrigger(
model_name="ridereview",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_ridereviewevent" ("content", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "ride_id", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rating", NEW."ride_id", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="INSERT",
pgid="pgtrigger_insert_insert_33237",
table="rides_ridereview",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="ridereview",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_ridereviewevent" ("content", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "ride_id", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rating", NEW."ride_id", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="UPDATE",
pgid="pgtrigger_update_update_90298",
table="rides_ridereview",
when="AFTER",
),
),
),
]

View File

@@ -1,294 +0,0 @@
# Generated by Django 5.1.4 on 2025-02-10 09:32
import django.db.models.deletion
import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("designers", "0001_initial"),
("manufacturers", "0001_initial"),
("parks", "0002_fix_pghistory_fields"), # This dependency is important for pghistory fields
("pghistory", "0006_delete_aggregateevent"),
("rides", "0002_ridemodel"),
]
operations = [
migrations.CreateModel(
name="RideEvent",
fields=[
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
("pgh_label", models.TextField(help_text="The event label.")),
("id", models.BigIntegerField()),
("name", models.CharField(max_length=255)),
("slug", models.SlugField(db_index=False, max_length=255)),
("description", models.TextField(blank=True)),
(
"category",
models.CharField(
blank=True,
choices=[
("", "Select ride type"),
("RC", "Roller Coaster"),
("DR", "Dark Ride"),
("FR", "Flat Ride"),
("WR", "Water Ride"),
("TR", "Transport"),
("OT", "Other"),
],
default="",
max_length=2,
),
),
(
"status",
models.CharField(
choices=[
("OPERATING", "Operating"),
("SBNO", "Standing But Not Operating"),
("CLOSING", "Closing"),
("CLOSED_PERM", "Permanently Closed"),
("UNDER_CONSTRUCTION", "Under Construction"),
("DEMOLISHED", "Demolished"),
("RELOCATED", "Relocated"),
],
default="OPERATING",
max_length=20,
),
),
(
"post_closing_status",
models.CharField(
blank=True,
choices=[
("SBNO", "Standing But Not Operating"),
("CLOSED_PERM", "Permanently Closed"),
],
help_text="Status to change to after closing date",
max_length=20,
null=True,
),
),
("opening_date", models.DateField(blank=True, null=True)),
("closing_date", models.DateField(blank=True, null=True)),
("status_since", models.DateField(blank=True, null=True)),
("min_height_in", models.PositiveIntegerField(blank=True, null=True)),
("max_height_in", models.PositiveIntegerField(blank=True, null=True)),
("capacity_per_hour", models.PositiveIntegerField(blank=True, null=True)),
("ride_duration_seconds", models.PositiveIntegerField(blank=True, null=True)),
(
"average_rating",
models.DecimalField(
blank=True, decimal_places=2, max_digits=3, null=True
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"designer",
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="designers.designer",
),
),
(
"manufacturer",
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="manufacturers.manufacturer",
),
),
(
"park",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="parks.park",
),
),
(
"park_area",
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="parks.parkarea",
),
),
(
"pgh_context",
models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="pghistory.context",
),
),
(
"pgh_obj",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="events",
to="rides.ride",
),
),
(
"ride_model",
models.ForeignKey(
blank=True,
db_constraint=False,
help_text="The specific model/type of this ride",
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="rides.ridemodel",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="RideModelEvent",
fields=[
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
("pgh_label", models.TextField(help_text="The event label.")),
("id", models.BigIntegerField()),
("name", models.CharField(max_length=255)),
("description", models.TextField(blank=True)),
(
"category",
models.CharField(
blank=True,
choices=[
("", "Select ride type"),
("RC", "Roller Coaster"),
("DR", "Dark Ride"),
("FR", "Flat Ride"),
("WR", "Water Ride"),
("TR", "Transport"),
("OT", "Other"),
],
default="",
max_length=2,
),
),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"manufacturer",
models.ForeignKey(
blank=True,
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="manufacturers.manufacturer",
),
),
(
"pgh_context",
models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="pghistory.context",
),
),
(
"pgh_obj",
models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="events",
to="rides.ridemodel",
),
),
],
options={
"abstract": False,
},
),
pgtrigger.migrations.AddTrigger(
model_name="ridemodel",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_ridemodelevent" ("category", "created_at", "description", "id", "manufacturer_id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", NEW."manufacturer_id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated_at"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="INSERT",
pgid="pgtrigger_insert_insert_0aaee",
table="rides_ridemodel",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="ridemodel",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_ridemodelevent" ("category", "created_at", "description", "id", "manufacturer_id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", NEW."manufacturer_id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated_at"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="UPDATE",
pgid="pgtrigger_update_update_0ca1a",
table="rides_ridemodel",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="ride",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_rideevent" ("average_rating", "capacity_per_hour", "category", "closing_date", "created_at", "description", "designer_id", "id", "manufacturer_id", "max_height_in", "min_height_in", "name", "opening_date", "park_area_id", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "post_closing_status", "ride_duration_seconds", "ride_model_id", "slug", "status", "status_since", "updated_at") VALUES (NEW."average_rating", NEW."capacity_per_hour", NEW."category", NEW."closing_date", NEW."created_at", NEW."description", NEW."designer_id", NEW."id", NEW."manufacturer_id", NEW."max_height_in", NEW."min_height_in", NEW."name", NEW."opening_date", NEW."park_area_id", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."post_closing_status", NEW."ride_duration_seconds", NEW."ride_model_id", NEW."slug", NEW."status", NEW."status_since", NEW."updated_at"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="INSERT",
pgid="pgtrigger_insert_insert_52074",
table="rides_ride",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="ride",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_rideevent" ("average_rating", "capacity_per_hour", "category", "closing_date", "created_at", "description", "designer_id", "id", "manufacturer_id", "max_height_in", "min_height_in", "name", "opening_date", "park_area_id", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "post_closing_status", "ride_duration_seconds", "ride_model_id", "slug", "status", "status_since", "updated_at") VALUES (NEW."average_rating", NEW."capacity_per_hour", NEW."category", NEW."closing_date", NEW."created_at", NEW."description", NEW."designer_id", NEW."id", NEW."manufacturer_id", NEW."max_height_in", NEW."min_height_in", NEW."name", NEW."opening_date", NEW."park_area_id", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."post_closing_status", NEW."ride_duration_seconds", NEW."ride_model_id", NEW."slug", NEW."status", NEW."status_since", NEW."updated_at"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="UPDATE",
pgid="pgtrigger_update_update_4917a",
table="rides_ride",
when="AFTER",
),
),
),
]

View File

@@ -0,0 +1,61 @@
# Generated by Django 5.0.7 on 2024-07-25 14:30
from django.db import migrations
def transfer_company_data(apps, schema_editor):
Company = apps.get_model('rides', 'Company')
Ride = apps.get_model('rides', 'Ride')
RideModel = apps.get_model('rides', 'RideModel')
with schema_editor.connection.cursor() as cursor:
cursor.execute("SELECT id, name, slug, description, website, founded_year, headquarters, rides_count, coasters_count FROM manufacturers_manufacturer")
for row in cursor.fetchall():
company, created = Company.objects.get_or_create(
slug=row,
defaults={
'name': row,
'description': row,
'website': row,
'founded_date': f'{row}-01-01' if row else None,
'headquarters': row,
'rides_count': row,
'coasters_count': row,
'roles': [Company.CompanyRole.MANUFACTURER]
}
)
if not created and Company.CompanyRole.MANUFACTURER not in company.roles:
company.roles.append(Company.CompanyRole.MANUFACTURER)
company.save()
Ride.objects.filter(manufacturer_id=row).update(manufacturer_id=company.id)
RideModel.objects.filter(manufacturer_id=row).update(manufacturer_id=company.id)
cursor.execute("SELECT id, name, slug, description, website, founded_date, headquarters FROM designers_designer")
for row in cursor.fetchall():
company, created = Company.objects.get_or_create(
slug=row,
defaults={
'name': row,
'description': row,
'website': row,
'founded_date': row,
'headquarters': row,
'roles': [Company.CompanyRole.DESIGNER]
}
)
if not created and Company.CompanyRole.DESIGNER not in company.roles:
company.roles.append(Company.CompanyRole.DESIGNER)
company.save()
Ride.objects.filter(designer_id=row).update(designer_id=company.id)
class Migration(migrations.Migration):
dependencies = [
('rides', '0002_ridereview_ridereviewevent_ridereview_insert_insert_and_more'),
]
operations = [
migrations.RunPython(transfer_company_data),
]

View File

@@ -0,0 +1,186 @@
# Generated by Django 5.1.4 on 2025-08-15 01:39
import django.contrib.gis.db.models.fields
import django.contrib.postgres.fields
import django.db.models.deletion
import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pghistory", "0006_delete_aggregateevent"),
("rides", "0003_transfer_company_data"),
]
operations = [
migrations.CreateModel(
name="CompanyEvent",
fields=[
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
("pgh_label", models.TextField(help_text="The event label.")),
("id", models.BigIntegerField()),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
("name", models.CharField(max_length=255)),
("slug", models.SlugField(db_index=False, max_length=255)),
(
"roles",
django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
("MANUFACTURER", "Ride Manufacturer"),
("DESIGNER", "Ride Designer"),
("OPERATOR", "Park Operator"),
("PROPERTY_OWNER", "Property Owner"),
],
max_length=20,
),
blank=True,
default=list,
size=None,
),
),
("description", models.TextField(blank=True)),
("website", models.URLField(blank=True)),
("founded_date", models.DateField(blank=True, null=True)),
("headquarters", models.CharField(blank=True, max_length=255)),
("rides_count", models.IntegerField(default=0)),
("coasters_count", models.IntegerField(default=0)),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="RideLocation",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"point",
django.contrib.gis.db.models.fields.PointField(
blank=True, null=True, srid=4326
),
),
(
"park_area",
models.CharField(
blank=True,
help_text="Area within the park where the ride is located",
max_length=100,
),
),
(
"notes",
models.TextField(blank=True, help_text="Specific location notes"),
),
],
options={
"verbose_name": "Ride Location",
"verbose_name_plural": "Ride Locations",
},
),
migrations.AddField(
model_name="company",
name="coasters_count",
field=models.IntegerField(default=0),
),
migrations.AddField(
model_name="company",
name="founded_date",
field=models.DateField(blank=True, null=True),
),
migrations.AddField(
model_name="company",
name="headquarters",
field=models.CharField(blank=True, max_length=255),
),
migrations.AlterField(
model_name="company",
name="roles",
field=django.contrib.postgres.fields.ArrayField(
base_field=models.CharField(
choices=[
("MANUFACTURER", "Ride Manufacturer"),
("DESIGNER", "Ride Designer"),
("OPERATOR", "Park Operator"),
("PROPERTY_OWNER", "Property Owner"),
],
max_length=20,
),
blank=True,
default=list,
size=None,
),
),
pgtrigger.migrations.AddTrigger(
model_name="company",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_companyevent" ("coasters_count", "created_at", "description", "founded_date", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rides_count", "roles", "slug", "updated_at", "website") VALUES (NEW."coasters_count", NEW."created_at", NEW."description", NEW."founded_date", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rides_count", NEW."roles", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="INSERT",
pgid="pgtrigger_insert_insert_e7194",
table="rides_company",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="company",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_companyevent" ("coasters_count", "created_at", "description", "founded_date", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rides_count", "roles", "slug", "updated_at", "website") VALUES (NEW."coasters_count", NEW."created_at", NEW."description", NEW."founded_date", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rides_count", NEW."roles", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="UPDATE",
pgid="pgtrigger_update_update_456a8",
table="rides_company",
when="AFTER",
),
),
),
migrations.AddField(
model_name="companyevent",
name="pgh_context",
field=models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="pghistory.context",
),
),
migrations.AddField(
model_name="companyevent",
name="pgh_obj",
field=models.ForeignKey(
db_constraint=False,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="events",
to="rides.company",
),
),
migrations.AddField(
model_name="ridelocation",
name="ride",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="location",
to="rides.ride",
),
),
]

View File

@@ -1,120 +0,0 @@
# Generated by Django 5.1.4 on 2025-02-10 09:33
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("rides", "0003_history_tracking"),
]
operations = [
migrations.CreateModel(
name="RollerCoasterStats",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
),
(
"length_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=7, null=True
),
),
(
"speed_mph",
models.DecimalField(
blank=True, decimal_places=2, max_digits=5, null=True
),
),
("inversions", models.PositiveIntegerField(default=0)),
(
"ride_time_seconds",
models.PositiveIntegerField(blank=True, null=True),
),
("track_type", models.CharField(blank=True, max_length=255)),
(
"track_material",
models.CharField(
blank=True,
choices=[
("STEEL", "Steel"),
("WOOD", "Wood"),
("HYBRID", "Hybrid"),
],
default="STEEL",
max_length=20,
),
),
(
"roller_coaster_type",
models.CharField(
blank=True,
choices=[
("SITDOWN", "Sit Down"),
("INVERTED", "Inverted"),
("FLYING", "Flying"),
("STANDUP", "Stand Up"),
("WING", "Wing"),
("DIVE", "Dive"),
("FAMILY", "Family"),
("WILD_MOUSE", "Wild Mouse"),
("SPINNING", "Spinning"),
("FOURTH_DIMENSION", "4th Dimension"),
("OTHER", "Other"),
],
default="SITDOWN",
max_length=20,
),
),
(
"max_drop_height_ft",
models.DecimalField(
blank=True, decimal_places=2, max_digits=6, null=True
),
),
(
"launch_type",
models.CharField(
choices=[
("CHAIN", "Chain Lift"),
("LSM", "LSM Launch"),
("HYDRAULIC", "Hydraulic Launch"),
("GRAVITY", "Gravity"),
("OTHER", "Other"),
],
default="CHAIN",
max_length=20,
),
),
("train_style", models.CharField(blank=True, max_length=255)),
("trains_count", models.PositiveIntegerField(blank=True, null=True)),
("cars_per_train", models.PositiveIntegerField(blank=True, null=True)),
("seats_per_car", models.PositiveIntegerField(blank=True, null=True)),
(
"ride",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="coaster_stats",
to="rides.ride",
),
),
],
options={
"verbose_name": "Roller Coaster Statistics",
"verbose_name_plural": "Roller Coaster Statistics",
},
),
]

View File

@@ -1,35 +0,0 @@
# Generated by Django 5.1.4 on 2025-02-10 10:38
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pghistory", "0006_delete_aggregateevent"),
("rides", "0004_rollercoasterstats"),
]
operations = [
migrations.AlterField(
model_name="rideevent",
name="pgh_context",
field=models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="pghistory.context",
),
),
migrations.AlterField(
model_name="ridemodelevent",
name="pgh_context",
field=models.ForeignKey(
db_constraint=False,
null=True,
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
to="pghistory.context",
),
),
]

View File

@@ -0,0 +1,61 @@
# Generated by Django 5.1.4 on 2025-08-15 01:41
import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
("rides", "0004_companyevent_ridelocation_company_coasters_count_and_more"),
("parks", "0004_remove_company_headquarters_companyheadquarters"),
]
operations = [
pgtrigger.migrations.RemoveTrigger(
model_name="company",
name="insert_insert",
),
pgtrigger.migrations.RemoveTrigger(
model_name="company",
name="update_update",
),
migrations.RemoveField(
model_name="company",
name="headquarters",
),
migrations.RemoveField(
model_name="companyevent",
name="headquarters",
),
pgtrigger.migrations.AddTrigger(
model_name="company",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_companyevent" ("coasters_count", "created_at", "description", "founded_date", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rides_count", "roles", "slug", "updated_at", "website") VALUES (NEW."coasters_count", NEW."created_at", NEW."description", NEW."founded_date", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rides_count", NEW."roles", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="INSERT",
pgid="pgtrigger_insert_insert_e7194",
table="rides_company",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="company",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_companyevent" ("coasters_count", "created_at", "description", "founded_date", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rides_count", "roles", "slug", "updated_at", "website") VALUES (NEW."coasters_count", NEW."created_at", NEW."description", NEW."founded_date", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rides_count", NEW."roles", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;',
hash="[AWS-SECRET-REMOVED]",
operation="UPDATE",
pgid="pgtrigger_update_update_456a8",
table="rides_company",
when="AFTER",
),
),
),
]

View File

@@ -1,76 +0,0 @@
# Generated by Django 5.1.4 on 2025-02-21 17:55
import pgtrigger.migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("parks", "0003_alter_park_id_alter_parkarea_id_and_more"),
("rides", "0005_fix_event_context_fields"),
]
operations = [
migrations.AlterModelOptions(
name="rideevent",
options={"managed": False},
),
migrations.AlterModelOptions(
name="ridemodelevent",
options={"managed": False},
),
pgtrigger.migrations.RemoveTrigger(
model_name="ride",
name="insert_insert",
),
pgtrigger.migrations.RemoveTrigger(
model_name="ride",
name="update_update",
),
pgtrigger.migrations.RemoveTrigger(
model_name="ridemodel",
name="insert_insert",
),
pgtrigger.migrations.RemoveTrigger(
model_name="ridemodel",
name="update_update",
),
migrations.AlterField(
model_name="ride",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterField(
model_name="ride",
name="status",
field=models.CharField(
choices=[
("", "Select status"),
("OPERATING", "Operating"),
("CLOSED_TEMP", "Temporarily Closed"),
("SBNO", "Standing But Not Operating"),
("CLOSING", "Closing"),
("CLOSED_PERM", "Permanently Closed"),
("UNDER_CONSTRUCTION", "Under Construction"),
("DEMOLISHED", "Demolished"),
("RELOCATED", "Relocated"),
],
default="OPERATING",
max_length=20,
),
),
migrations.AlterField(
model_name="ridemodel",
name="id",
field=models.BigAutoField(
auto_created=True, primary_key=True, serialize=False, verbose_name="ID"
),
),
migrations.AlterUniqueTogether(
name="ride",
unique_together={("park", "slug")},
),
]

View File

@@ -0,0 +1,92 @@
# Generated by Django 5.1.4 on 2025-08-15 14:16
import django.contrib.gis.db.models.fields
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("rides", "0005_remove_company_insert_insert_and_more"),
]
operations = [
migrations.AlterModelOptions(
name="ridelocation",
options={
"ordering": ["ride__name"],
"verbose_name": "Ride Location",
"verbose_name_plural": "Ride Locations",
},
),
migrations.RemoveField(
model_name="ridelocation",
name="notes",
),
migrations.AddField(
model_name="ridelocation",
name="accessibility_notes",
field=models.TextField(
blank=True,
help_text="Information about accessible entrances, wheelchair access, etc.",
),
),
migrations.AddField(
model_name="ridelocation",
name="created_at",
field=models.DateTimeField(
auto_now_add=True, default=django.utils.timezone.now
),
preserve_default=False,
),
migrations.AddField(
model_name="ridelocation",
name="entrance_notes",
field=models.TextField(
blank=True,
help_text="Directions to ride entrance, queue location, or navigation tips",
),
),
migrations.AddField(
model_name="ridelocation",
name="updated_at",
field=models.DateTimeField(auto_now=True),
),
migrations.AlterField(
model_name="ridelocation",
name="park_area",
field=models.CharField(
blank=True,
db_index=True,
help_text="Themed area or land within the park (e.g., 'Frontierland', 'Tomorrowland')",
max_length=100,
),
),
migrations.AlterField(
model_name="ridelocation",
name="point",
field=django.contrib.gis.db.models.fields.PointField(
blank=True,
help_text="Geographic coordinates for ride location (longitude, latitude)",
null=True,
srid=4326,
),
),
migrations.AlterField(
model_name="ridelocation",
name="ride",
field=models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
related_name="ride_location",
to="rides.ride",
),
),
migrations.AddIndex(
model_name="ridelocation",
index=models.Index(
fields=["park_area"], name="rides_ridel_park_ar_26c90c_idx"
),
),
]

View File

@@ -1,45 +0,0 @@
# Generated by Django 5.1.4 on 2025-07-04 15:26
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("manufacturers", "0001_initial"),
("rides", "0006_alter_rideevent_options_alter_ridemodelevent_options_and_more"),
]
operations = [
migrations.AlterField(
model_name="ride",
name="manufacturer",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="rides",
to="manufacturers.manufacturer",
),
),
migrations.AlterField(
model_name="ridemodel",
name="manufacturer",
field=models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
related_name="ride_models",
to="manufacturers.manufacturer",
),
),
migrations.AlterModelTable(
name="rideevent",
table="rides_rideevent",
),
migrations.AlterModelTable(
name="ridemodelevent",
table="rides_ridemodelevent",
),
]

View File

@@ -0,0 +1,66 @@
# Generated by Django 5.1.4 on 2025-08-15 14:18
from django.db import migrations, models
from django.contrib.gis.db import models as gis_models
import django.utils.timezone
class Migration(migrations.Migration):
dependencies = [
("rides", "0006_alter_ridelocation_options_remove_ridelocation_notes_and_more"),
]
operations = [
# Add new fields according to our enhanced model
migrations.AddField(
model_name='ridelocation',
name='entrance_notes',
field=models.TextField(blank=True, help_text='Directions to ride entrance, queue location, or navigation tips'),
),
migrations.AddField(
model_name='ridelocation',
name='accessibility_notes',
field=models.TextField(blank=True, help_text='Information about accessible entrances, wheelchair access, etc.'),
),
migrations.AddField(
model_name='ridelocation',
name='created_at',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
migrations.AddField(
model_name='ridelocation',
name='updated_at',
field=models.DateTimeField(auto_now=True),
),
# Update existing fields
migrations.AlterField(
model_name='ridelocation',
name='park_area',
field=models.CharField(blank=True, db_index=True, help_text="Themed area or land within the park (e.g., 'Frontierland', 'Tomorrowland')", max_length=100),
),
migrations.AlterField(
model_name='ridelocation',
name='point',
field=gis_models.PointField(blank=True, help_text='Geographic coordinates for ride location (longitude, latitude)', null=True, srid=4326),
),
migrations.AlterField(
model_name='ridelocation',
name='ride',
field=models.OneToOneField(on_delete=models.CASCADE, related_name='ride_location', to='rides.ride'),
),
# Update Meta options
migrations.AlterModelOptions(
name='ridelocation',
options={'ordering': ['ride__name'], 'verbose_name': 'Ride Location', 'verbose_name_plural': 'Ride Locations'},
),
# Add index for park_area if it doesn't exist
migrations.AddIndex(
model_name='ridelocation',
index=models.Index(fields=['park_area'], name='rides_ridelocation_park_area_idx'),
),
]

View File

@@ -1,9 +1,10 @@
from django.db import models
from django.utils.text import slugify
from django.contrib.contenttypes.fields import GenericRelation
from history_tracking.models import TrackedModel, DiffMixin
from manufacturers.models import Manufacturer
from core.history import TrackedModel, DiffMixin
from .events import get_ride_display_changes, get_ride_model_display_changes
import pghistory
from .company import Company
# Shared choices that will be used by multiple models
CATEGORY_CHOICES = [
@@ -45,8 +46,6 @@ class RideEvent(models.Model, DiffMixin):
# Foreign keys as IDs
park_id = models.BigIntegerField()
park_area_id = models.BigIntegerField(null=True)
manufacturer_id = models.BigIntegerField(null=True)
designer_id = models.BigIntegerField(null=True)
ride_model_id = models.BigIntegerField(null=True)
# Context fields
@@ -110,11 +109,12 @@ class RideModel(TrackedModel):
"""
name = models.CharField(max_length=255)
manufacturer = models.ForeignKey(
Manufacturer,
Company,
on_delete=models.SET_NULL,
related_name='ride_models',
null=True,
blank=True
blank=True,
limit_choices_to={'roles__contains': [Company.CompanyRole.MANUFACTURER]}
)
description = models.TextField(blank=True)
category = models.CharField(
@@ -172,18 +172,20 @@ class Ride(TrackedModel):
blank=True
)
manufacturer = models.ForeignKey(
Manufacturer,
Company,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='rides'
related_name='manufactured_rides',
limit_choices_to={'roles__contains': [Company.CompanyRole.MANUFACTURER]}
)
designer = models.ForeignKey(
'designers.Designer',
Company,
on_delete=models.SET_NULL,
related_name='rides',
related_name='designed_rides',
null=True,
blank=True
blank=True,
limit_choices_to={'roles__contains': [Company.CompanyRole.DESIGNER]}
)
ride_model = models.ForeignKey(
'RideModel',
@@ -219,7 +221,6 @@ class Ride(TrackedModel):
blank=True
)
photos = GenericRelation('media.Photo')
reviews = GenericRelation('reviews.Review')
class Meta:
ordering = ['name']

3
rides/models/__init__.py Normal file
View File

@@ -0,0 +1,3 @@
from .rides import *
from .reviews import *
from .location import *

77
rides/models/company.py Normal file
View File

@@ -0,0 +1,77 @@
import pghistory
from django.contrib.postgres.fields import ArrayField
from django.db import models
from django.urls import reverse
from django.utils.text import slugify
from core.history import HistoricalSlug
from core.models import TrackedModel
@pghistory.track()
class Company(TrackedModel):
class CompanyRole(models.TextChoices):
MANUFACTURER = 'MANUFACTURER', 'Ride Manufacturer'
DESIGNER = 'DESIGNER', 'Ride Designer'
OPERATOR = 'OPERATOR', 'Park Operator'
PROPERTY_OWNER = 'PROPERTY_OWNER', 'Property Owner'
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255, unique=True)
roles = ArrayField(
models.CharField(max_length=20, choices=CompanyRole.choices),
default=list,
blank=True
)
description = models.TextField(blank=True)
website = models.URLField(blank=True)
# General company info
founded_date = models.DateField(null=True, blank=True)
# Manufacturer-specific fields
rides_count = models.IntegerField(default=0)
coasters_count = models.IntegerField(default=0)
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
def get_absolute_url(self):
# This will need to be updated to handle different roles
return reverse('companies:detail', kwargs={'slug': self.slug})
return '#'
@classmethod
def get_by_slug(cls, slug):
"""Get company by current or historical slug"""
try:
return cls.objects.get(slug=slug), False
except cls.DoesNotExist:
# Check pghistory first
history_model = cls.get_history_model()
history_entry = (
history_model.objects.filter(slug=slug)
.order_by('-pgh_created_at')
.first()
)
if history_entry:
return cls.objects.get(id=history_entry.pgh_obj_id), True
# Check manual slug history as fallback
try:
historical = HistoricalSlug.objects.get(
content_type__model='company',
slug=slug
)
return cls.objects.get(pk=historical.object_id), True
except (HistoricalSlug.DoesNotExist, cls.DoesNotExist):
raise cls.DoesNotExist("No company found with this slug")
class Meta:
ordering = ['name']
verbose_name_plural = 'Companies'

125
rides/models/location.py Normal file
View File

@@ -0,0 +1,125 @@
from django.contrib.gis.db import models as gis_models
from django.db import models
from django.contrib.gis.geos import Point
class RideLocation(models.Model):
"""
Lightweight location tracking for individual rides within parks.
Optional coordinates with focus on practical navigation information.
"""
# Relationships
ride = models.OneToOneField(
'rides.Ride',
on_delete=models.CASCADE,
related_name='ride_location'
)
# Optional Spatial Data - keep it simple with single point
point = gis_models.PointField(
srid=4326,
null=True,
blank=True,
help_text="Geographic coordinates for ride location (longitude, latitude)"
)
# Park Area Information
park_area = models.CharField(
max_length=100,
blank=True,
db_index=True,
help_text="Themed area or land within the park (e.g., 'Frontierland', 'Tomorrowland')"
)
# General notes field to match database schema
notes = models.TextField(
blank=True,
help_text="General location notes"
)
# Navigation and Entrance Information
entrance_notes = models.TextField(
blank=True,
help_text="Directions to ride entrance, queue location, or navigation tips"
)
# Accessibility Information
accessibility_notes = models.TextField(
blank=True,
help_text="Information about accessible entrances, wheelchair access, etc."
)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@property
def latitude(self):
"""Return latitude from point field for backward compatibility."""
if self.point:
return self.point.y
return None
@property
def longitude(self):
"""Return longitude from point field for backward compatibility."""
if self.point:
return self.point.x
return None
@property
def coordinates(self):
"""Return (latitude, longitude) tuple."""
if self.point:
return (self.latitude, self.longitude)
return (None, None)
@property
def has_coordinates(self):
"""Check if coordinates are set."""
return self.point is not None
def set_coordinates(self, latitude, longitude):
"""
Set the location's point from latitude and longitude coordinates.
Validates coordinate ranges.
"""
if latitude is None or longitude is None:
self.point = None
return
if not -90 <= latitude <= 90:
raise ValueError("Latitude must be between -90 and 90.")
if not -180 <= longitude <= 180:
raise ValueError("Longitude must be between -180 and 180.")
self.point = Point(longitude, latitude, srid=4326)
def distance_to_park_location(self):
"""
Calculate distance to parent park's location if both have coordinates.
Returns distance in kilometers.
"""
if not self.point:
return None
park_location = getattr(self.ride.park, 'location', None)
if not park_location or not park_location.point:
return None
# Use geodetic distance calculation which returns meters, convert to km
distance_m = self.point.distance(park_location.point)
return distance_m / 1000.0
def __str__(self):
area_str = f" in {self.park_area}" if self.park_area else ""
return f"Location for {self.ride.name}{area_str}"
class Meta:
verbose_name = "Ride Location"
verbose_name_plural = "Ride Locations"
ordering = ['ride__name']
indexes = [
models.Index(fields=['park_area']),
# Spatial index will be created automatically for PostGIS PointField
]

49
rides/models/reviews.py Normal file
View File

@@ -0,0 +1,49 @@
from django.db import models
from django.core.validators import MinValueValidator, MaxValueValidator
from core.history import TrackedModel
import pghistory
@pghistory.track()
class RideReview(TrackedModel):
"""
A review of a ride.
"""
ride = models.ForeignKey(
'rides.Ride',
on_delete=models.CASCADE,
related_name='reviews'
)
user = models.ForeignKey(
'accounts.User',
on_delete=models.CASCADE,
related_name='ride_reviews'
)
rating = models.PositiveSmallIntegerField(
validators=[MinValueValidator(1), MaxValueValidator(10)]
)
title = models.CharField(max_length=200)
content = models.TextField()
visit_date = models.DateField()
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
# Moderation
is_published = models.BooleanField(default=True)
moderation_notes = models.TextField(blank=True)
moderated_by = models.ForeignKey(
'accounts.User',
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='moderated_ride_reviews'
)
moderated_at = models.DateTimeField(null=True, blank=True)
class Meta:
ordering = ['-created_at']
unique_together = ['ride', 'user']
def __str__(self):
return f"Review of {self.ride.name} by {self.user.username}"

239
rides/models/rides.py Normal file
View File

@@ -0,0 +1,239 @@
from django.db import models
from django.utils.text import slugify
from django.contrib.contenttypes.fields import GenericRelation
from core.models import TrackedModel
from .company import Company
# Shared choices that will be used by multiple models
CATEGORY_CHOICES = [
('', 'Select ride type'),
('RC', 'Roller Coaster'),
('DR', 'Dark Ride'),
('FR', 'Flat Ride'),
('WR', 'Water Ride'),
('TR', 'Transport'),
('OT', 'Other'),
]
class RideModel(TrackedModel):
"""
Represents a specific model/type of ride that can be manufactured by different companies.
For example: B&M Dive Coaster, Vekoma Boomerang, etc.
"""
name = models.CharField(max_length=255)
manufacturer = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
related_name='ride_models',
null=True,
blank=True,
limit_choices_to={'roles__contains': ['MANUFACTURER']},
)
description = models.TextField(blank=True)
category = models.CharField(
max_length=2,
choices=CATEGORY_CHOICES,
default='',
blank=True
)
class Meta:
ordering = ['manufacturer', 'name']
unique_together = ['manufacturer', 'name']
def __str__(self) -> str:
return self.name if not self.manufacturer else f"{self.manufacturer.name} {self.name}"
class Ride(TrackedModel):
"""Model for individual ride installations at parks"""
STATUS_CHOICES = [
('', 'Select status'),
('OPERATING', 'Operating'),
('CLOSED_TEMP', 'Temporarily Closed'),
('SBNO', 'Standing But Not Operating'),
('CLOSING', 'Closing'),
('CLOSED_PERM', 'Permanently Closed'),
('UNDER_CONSTRUCTION', 'Under Construction'),
('DEMOLISHED', 'Demolished'),
('RELOCATED', 'Relocated'),
]
POST_CLOSING_STATUS_CHOICES = [
('SBNO', 'Standing But Not Operating'),
('CLOSED_PERM', 'Permanently Closed'),
]
name = models.CharField(max_length=255)
slug = models.SlugField(max_length=255)
description = models.TextField(blank=True)
park = models.ForeignKey(
'parks.Park',
on_delete=models.CASCADE,
related_name='rides'
)
park_area = models.ForeignKey(
'parks.ParkArea',
on_delete=models.SET_NULL,
related_name='rides',
null=True,
blank=True
)
category = models.CharField(
max_length=2,
choices=CATEGORY_CHOICES,
default='',
blank=True
)
manufacturer = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name='manufactured_rides',
limit_choices_to={'roles__contains': ['MANUFACTURER']},
)
designer = models.ForeignKey(
Company,
on_delete=models.SET_NULL,
related_name='designed_rides',
null=True,
blank=True,
limit_choices_to={'roles__contains': ['DESIGNER']},
)
ride_model = models.ForeignKey(
'RideModel',
on_delete=models.SET_NULL,
related_name='rides',
null=True,
blank=True,
help_text="The specific model/type of this ride"
)
status = models.CharField(
max_length=20,
choices=STATUS_CHOICES,
default='OPERATING'
)
post_closing_status = models.CharField(
max_length=20,
choices=POST_CLOSING_STATUS_CHOICES,
null=True,
blank=True,
help_text="Status to change to after closing date"
)
opening_date = models.DateField(null=True, blank=True)
closing_date = models.DateField(null=True, blank=True)
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)
capacity_per_hour = models.PositiveIntegerField(null=True, blank=True)
ride_duration_seconds = models.PositiveIntegerField(null=True, blank=True)
average_rating = models.DecimalField(
max_digits=3,
decimal_places=2,
null=True,
blank=True
)
photos = GenericRelation('media.Photo')
class Meta:
ordering = ['name']
unique_together = ['park', 'slug']
def __str__(self) -> str:
return f"{self.name} at {self.park.name}"
def save(self, *args, **kwargs) -> None:
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
class RollerCoasterStats(models.Model):
"""Model for tracking roller coaster specific statistics"""
TRACK_MATERIAL_CHOICES = [
('STEEL', 'Steel'),
('WOOD', 'Wood'),
('HYBRID', 'Hybrid'),
]
COASTER_TYPE_CHOICES = [
('SITDOWN', 'Sit Down'),
('INVERTED', 'Inverted'),
('FLYING', 'Flying'),
('STANDUP', 'Stand Up'),
('WING', 'Wing'),
('DIVE', 'Dive'),
('FAMILY', 'Family'),
('WILD_MOUSE', 'Wild Mouse'),
('SPINNING', 'Spinning'),
('FOURTH_DIMENSION', '4th Dimension'),
('OTHER', 'Other'),
]
LAUNCH_CHOICES = [
('CHAIN', 'Chain Lift'),
('LSM', 'LSM Launch'),
('HYDRAULIC', 'Hydraulic Launch'),
('GRAVITY', 'Gravity'),
('OTHER', 'Other'),
]
ride = models.OneToOneField(
Ride,
on_delete=models.CASCADE,
related_name='coaster_stats'
)
height_ft = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True
)
length_ft = models.DecimalField(
max_digits=7,
decimal_places=2,
null=True,
blank=True
)
speed_mph = models.DecimalField(
max_digits=5,
decimal_places=2,
null=True,
blank=True
)
inversions = models.PositiveIntegerField(default=0)
ride_time_seconds = models.PositiveIntegerField(null=True, blank=True)
track_type = models.CharField(max_length=255, blank=True)
track_material = models.CharField(
max_length=20,
choices=TRACK_MATERIAL_CHOICES,
default='STEEL',
blank=True
)
roller_coaster_type = models.CharField(
max_length=20,
choices=COASTER_TYPE_CHOICES,
default='SITDOWN',
blank=True
)
max_drop_height_ft = models.DecimalField(
max_digits=6,
decimal_places=2,
null=True,
blank=True
)
launch_type = models.CharField(
max_length=20,
choices=LAUNCH_CHOICES,
default='CHAIN'
)
train_style = models.CharField(max_length=255, blank=True)
trains_count = models.PositiveIntegerField(null=True, blank=True)
cars_per_train = models.PositiveIntegerField(null=True, blank=True)
seats_per_car = models.PositiveIntegerField(null=True, blank=True)
class Meta:
verbose_name = 'Roller Coaster Statistics'
verbose_name_plural = 'Roller Coaster Statistics'
def __str__(self) -> str:
return f"Stats for {self.ride.name}"

View File

@@ -19,18 +19,13 @@ urlpatterns = [
views.RideUpdateView.as_view(),
name="ride_update"
),
path(
"search/companies/",
views.search_companies,
name="search_companies"
),
# Search endpoints
path(
"search/manufacturers/",
views.search_manufacturers,
name="search_manufacturers"
),
path(
"search/designers/",
views.search_designers,
name="search_designers"
),
path(
"search/models/",
views.search_ride_models,

View File

@@ -0,0 +1,3 @@
{% for company in companies %}
<div>{{ company.name }}</div>
{% endfor %}

View File

@@ -46,21 +46,16 @@ urlpatterns = [
),
# Search endpoints (must come before slug patterns)
path(
"search/manufacturers/",
views.search_manufacturers,
name="search_manufacturers"
),
path(
"search/designers/",
views.search_designers,
name="search_designers"
),
path(
"search/models/",
views.search_ride_models,
name="search_ride_models"
),
path(
"search/companies/",
views.search_companies,
name="search_companies"
),
# HTMX endpoints (must come before slug patterns)
path(

View File

@@ -9,16 +9,14 @@ from django.contrib import messages
from django.http import HttpRequest, HttpResponse, Http404
from django.db.models import Count
from .models import (
Ride, RollerCoasterStats, RideModel, RideEvent,
CATEGORY_CHOICES
Ride, RollerCoasterStats, RideModel,
CATEGORY_CHOICES, Company
)
from .forms import RideForm
from parks.models import Park
from core.views import SlugRedirectMixin
from moderation.mixins import EditSubmissionMixin, PhotoSubmissionMixin, HistoryMixin
from moderation.models import EditSubmission
from manufacturers.models import Manufacturer
from designers.models import Designer
class ParkContextRequired:
@@ -64,11 +62,6 @@ class RideDetailView(HistoryMixin, DetailView):
context['park_slug'] = self.kwargs['park_slug']
context['park'] = self.object.park
# Add history records
context['history'] = RideEvent.objects.filter(
pgh_obj_id=self.object.id
).order_by('-pgh_created_at')
return context
@@ -107,9 +100,9 @@ class RideCreateView(LoginRequiredMixin, ParkContextRequired, CreateView):
if manufacturer_name and not form.cleaned_data.get('manufacturer'):
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Manufacturer),
content_type=ContentType.objects.get_for_model(Company),
submission_type="CREATE",
changes={"name": manufacturer_name},
changes={"name": manufacturer_name, "roles": ["MANUFACTURER"]},
)
# Check for new designer
@@ -117,9 +110,9 @@ class RideCreateView(LoginRequiredMixin, ParkContextRequired, CreateView):
if designer_name and not form.cleaned_data.get('designer'):
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Designer),
content_type=ContentType.objects.get_for_model(Company),
submission_type="CREATE",
changes={"name": designer_name},
changes={"name": designer_name, "roles": ["DESIGNER"]},
)
# Check for new ride model
@@ -179,9 +172,9 @@ class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixi
if manufacturer_name and not form.cleaned_data.get('manufacturer'):
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Manufacturer),
content_type=ContentType.objects.get_for_model(Company),
submission_type="CREATE",
changes={"name": manufacturer_name}
changes={"name": manufacturer_name, "roles": ["MANUFACTURER"]}
)
# Check for new designer
@@ -189,9 +182,9 @@ class RideUpdateView(LoginRequiredMixin, ParkContextRequired, EditSubmissionMixi
if designer_name and not form.cleaned_data.get('designer'):
EditSubmission.objects.create(
user=self.request.user,
content_type=ContentType.objects.get_for_model(Designer),
content_type=ContentType.objects.get_for_model(Company),
submission_type="CREATE",
changes={"name": designer_name}
changes={"name": designer_name, "roles": ["DESIGNER"]}
)
# Check for new ride model
@@ -314,40 +307,26 @@ class SingleCategoryListView(ListView):
ParkSingleCategoryListView = SingleCategoryListView
def search_manufacturers(request: HttpRequest) -> HttpResponse:
"""Search manufacturers and return results for HTMX"""
query = request.GET.get("q", "").strip()
# Show all manufacturers on click, filter on input
manufacturers = Manufacturer.objects.all().order_by("name")
def search_companies(request: HttpRequest) -> HttpResponse:
"""Search companies and return results for HTMX"""
query = request.GET.get("q", "").strip()
role = request.GET.get("role", "").upper()
companies = Company.objects.all().order_by("name")
if role:
companies = companies.filter(roles__contains=[role])
if query:
manufacturers = manufacturers.filter(name__icontains=query)
manufacturers = manufacturers[:10]
companies = companies.filter(name__icontains=query)
companies = companies[:10]
return render(
request,
"rides/partials/manufacturer_search_results.html",
{"manufacturers": manufacturers, "search_term": query},
"rides/partials/company_search_results.html",
{"companies": companies, "search_term": query},
)
def search_designers(request: HttpRequest) -> HttpResponse:
"""Search designers and return results for HTMX"""
query = request.GET.get("q", "").strip()
# Show all designers on click, filter on input
designers = Designer.objects.all().order_by("name")
if query:
designers = designers.filter(name__icontains=query)
designers = designers[:10]
return render(
request,
"rides/partials/designer_search_results.html",
{"designers": designers, "search_term": query},
)
def search_ride_models(request: HttpRequest) -> HttpResponse:
"""Search ride models and return results for HTMX"""
query = request.GET.get("q", "").strip()