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

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

View File

@@ -1,12 +1,13 @@
# Generated by Django 5.2.5 on 2025-08-26 17:39
import apps.rides.models.media
import django.db.models.deletion
import pgtrigger.compiler
import pgtrigger.migrations
from django.conf import settings
from django.db import migrations, models
import apps.rides.models.media
class Migration(migrations.Migration):
dependencies = [

View File

@@ -6,36 +6,36 @@ in the previous migration. These fields enable efficient hybrid filtering by
pre-computing commonly filtered and searched data.
"""
from django.db import migrations
import pghistory
from django.db import migrations
def populate_computed_fields(apps, schema_editor):
"""Populate computed fields for all existing rides."""
Ride = apps.get_model('rides', 'Ride')
# Disable pghistory triggers during bulk operations to avoid performance issues
with pghistory.context(disable=True):
rides = list(Ride.objects.all().select_related(
'park', 'park__location', 'park_area', 'manufacturer', 'designer', 'ride_model'
))
for ride in rides:
# Extract opening year from opening_date
if ride.opening_date:
ride.opening_year = ride.opening_date.year
else:
ride.opening_year = None
# Build comprehensive search text
search_parts = []
# Basic ride info
if ride.name:
search_parts.append(ride.name)
if ride.description:
search_parts.append(ride.description)
# Park info
if ride.park:
search_parts.append(ride.park.name)
@@ -46,11 +46,11 @@ def populate_computed_fields(apps, schema_editor):
search_parts.append(ride.park.location.state)
if ride.park.location.country:
search_parts.append(ride.park.location.country)
# Park area
if ride.park_area:
search_parts.append(ride.park_area.name)
# Category
if ride.category:
category_choices = [
@@ -65,7 +65,7 @@ def populate_computed_fields(apps, schema_editor):
category_display = dict(category_choices).get(ride.category, '')
if category_display:
search_parts.append(category_display)
# Status
if ride.status:
status_choices = [
@@ -82,21 +82,21 @@ def populate_computed_fields(apps, schema_editor):
status_display = dict(status_choices).get(ride.status, '')
if status_display:
search_parts.append(status_display)
# Companies
if ride.manufacturer:
search_parts.append(ride.manufacturer.name)
if ride.designer:
search_parts.append(ride.designer.name)
# Ride model
if ride.ride_model:
search_parts.append(ride.ride_model.name)
if ride.ride_model.manufacturer:
search_parts.append(ride.ride_model.manufacturer.name)
ride.search_text = ' '.join(filter(None, search_parts)).lower()
# Bulk update all rides
Ride.objects.bulk_update(rides, ['opening_year', 'search_text'], batch_size=1000)
@@ -104,7 +104,7 @@ def populate_computed_fields(apps, schema_editor):
def reverse_populate_computed_fields(apps, schema_editor):
"""Clear computed fields (reverse operation)."""
Ride = apps.get_model('rides', 'Ride')
# Disable pghistory triggers during bulk operations
with pghistory.context(disable=True):
Ride.objects.all().update(opening_year=None, search_text='')

View File

@@ -28,151 +28,151 @@ class Migration(migrations.Migration):
"CREATE INDEX rides_ride_park_category_idx ON rides_ride (park_id, category) WHERE category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_category_idx;"
),
# Composite index for park + status filtering (common)
migrations.RunSQL(
"CREATE INDEX rides_ride_park_status_idx ON rides_ride (park_id, status);",
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_status_idx;"
),
# Composite index for category + status filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_category_status_idx ON rides_ride (category, status) WHERE category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_category_status_idx;"
),
# Composite index for manufacturer + category
migrations.RunSQL(
"CREATE INDEX rides_ride_manufacturer_category_idx ON rides_ride (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL AND category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_manufacturer_category_idx;"
),
# Composite index for opening year + category (for timeline filtering)
migrations.RunSQL(
"CREATE INDEX rides_ride_opening_year_category_idx ON rides_ride (opening_year, category) WHERE opening_year IS NOT NULL AND category != '';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_opening_year_category_idx;"
),
# Partial index for operating rides only (most common filter)
migrations.RunSQL(
"CREATE INDEX rides_ride_operating_only_idx ON rides_ride (park_id, category, opening_year) WHERE status = 'OPERATING';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_operating_only_idx;"
),
# Partial index for roller coasters only (popular category)
migrations.RunSQL(
"CREATE INDEX rides_ride_roller_coasters_idx ON rides_ride (park_id, status, opening_year) WHERE category = 'RC';",
reverse_sql="DROP INDEX IF EXISTS rides_ride_roller_coasters_idx;"
),
# Covering index for list views (includes commonly displayed fields)
migrations.RunSQL(
"CREATE INDEX rides_ride_list_covering_idx ON rides_ride (park_id, category, status) INCLUDE (name, opening_date, average_rating);",
reverse_sql="DROP INDEX IF EXISTS rides_ride_list_covering_idx;"
),
# GIN index for full-text search on computed search_text field
migrations.RunSQL(
"CREATE INDEX rides_ride_search_text_gin_idx ON rides_ride USING gin(to_tsvector('english', search_text));",
reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_gin_idx;"
),
# Trigram index for fuzzy text search
migrations.RunSQL(
"CREATE INDEX rides_ride_search_text_trgm_idx ON rides_ride USING gin(search_text gin_trgm_ops);",
reverse_sql="DROP INDEX IF EXISTS rides_ride_search_text_trgm_idx;"
),
# Index for rating-based filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_rating_idx ON rides_ride (average_rating) WHERE average_rating IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_rating_idx;"
),
# Index for capacity-based filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_capacity_idx ON rides_ride (capacity_per_hour) WHERE capacity_per_hour IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_capacity_idx;"
),
# Index for height requirement filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_height_req_idx ON rides_ride (min_height_in, max_height_in) WHERE min_height_in IS NOT NULL OR max_height_in IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_height_req_idx;"
),
# Composite index for ride model filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_model_manufacturer_idx ON rides_ride (ride_model_id, manufacturer_id) WHERE ride_model_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_model_manufacturer_idx;"
),
# Index for designer filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_designer_idx ON rides_ride (designer_id, category) WHERE designer_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_designer_idx;"
),
# Index for park area filtering
migrations.RunSQL(
"CREATE INDEX rides_ride_park_area_idx ON rides_ride (park_area_id, status) WHERE park_area_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ride_park_area_idx;"
),
# Roller coaster stats indexes for performance
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_height_idx ON rides_rollercoasterstats (height_ft) WHERE height_ft IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_height_idx;"
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_speed_idx ON rides_rollercoasterstats (speed_mph) WHERE speed_mph IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_speed_idx;"
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_inversions_idx ON rides_rollercoasterstats (inversions);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_inversions_idx;"
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_type_material_idx ON rides_rollercoasterstats (roller_coaster_type, track_material);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_type_material_idx;"
),
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_launch_type_idx ON rides_rollercoasterstats (launch_type);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_launch_type_idx;"
),
# Composite index for complex roller coaster filtering
migrations.RunSQL(
"CREATE INDEX rides_rollercoasterstats_complex_idx ON rides_rollercoasterstats (roller_coaster_type, track_material, launch_type) INCLUDE (height_ft, speed_mph, inversions);",
reverse_sql="DROP INDEX IF EXISTS rides_rollercoasterstats_complex_idx;"
),
# Index for ride model filtering and search
migrations.RunSQL(
"CREATE INDEX rides_ridemodel_manufacturer_category_idx ON rides_ridemodel (manufacturer_id, category) WHERE manufacturer_id IS NOT NULL;",
reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_manufacturer_category_idx;"
),
migrations.RunSQL(
"CREATE INDEX rides_ridemodel_name_trgm_idx ON rides_ridemodel USING gin(name gin_trgm_ops);",
reverse_sql="DROP INDEX IF EXISTS rides_ridemodel_name_trgm_idx;"
),
# Index for company role-based filtering
migrations.RunSQL(
"CREATE INDEX rides_company_manufacturer_role_idx ON rides_company USING gin(roles) WHERE 'MANUFACTURER' = ANY(roles);",
reverse_sql="DROP INDEX IF EXISTS rides_company_manufacturer_role_idx;"
),
migrations.RunSQL(
"CREATE INDEX rides_company_designer_role_idx ON rides_company USING gin(roles) WHERE 'DESIGNER' = ANY(roles);",
reverse_sql="DROP INDEX IF EXISTS rides_company_designer_role_idx;"
),
# Ensure trigram extension is available for fuzzy search
migrations.RunSQL(
"CREATE EXTENSION IF NOT EXISTS pg_trgm;",

View File

@@ -1,9 +1,10 @@
# Generated by Django 5.2.5 on 2025-09-15 17:35
import apps.core.choices.fields
import django.contrib.postgres.fields
from django.db import migrations, models
import apps.core.choices.fields
class Migration(migrations.Migration):

View File

@@ -1,9 +1,10 @@
# Generated by Django 5.2.5 on 2025-09-15 18:07
import apps.core.choices.fields
import django.contrib.postgres.fields
from django.db import migrations
import apps.core.choices.fields
class Migration(migrations.Migration):

View File

@@ -1,8 +1,9 @@
# Generated by Django 5.2.5 on 2025-09-15 19:06
import apps.core.choices.fields
from django.db import migrations
import apps.core.choices.fields
class Migration(migrations.Migration):

View File

@@ -1,10 +1,11 @@
# Generated by Django 5.2.5 on 2025-09-17 01:25
import apps.core.choices.fields
import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations
import apps.core.choices.fields
class Migration(migrations.Migration):

View File

@@ -1,9 +1,10 @@
# Generated by Django 5.1.3 on 2025-12-21 03:20
import apps.core.state_machine.fields
import django.db.models.deletion
from django.db import migrations, models
import apps.core.state_machine.fields
class Migration(migrations.Migration):

View File

@@ -1,10 +1,11 @@
# Generated by Django 5.1.6 on 2025-12-26 14:10
import apps.core.choices.fields
import django.contrib.postgres.fields
import django.db.models.deletion
from django.db import migrations, models
import apps.core.choices.fields
class Migration(migrations.Migration):

View File

@@ -0,0 +1,533 @@
# Generated by Django 5.2.9 on 2025-12-27 20:58
import django.db.models.deletion
import pgtrigger.compiler
import pgtrigger.migrations
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("pghistory", "0007_auto_20250421_0444"),
("rides", "0028_ridecredit_ridecreditevent_ridecredit_insert_insert_and_more"),
]
operations = [
migrations.CreateModel(
name="DarkRideStats",
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)),
("scene_count", models.PositiveIntegerField(default=0, help_text="Number of themed scenes")),
(
"animatronic_count",
models.PositiveIntegerField(default=0, help_text="Number of animatronic figures"),
),
(
"has_projection_technology",
models.BooleanField(default=False, help_text="Whether the ride uses projection mapping or screens"),
),
(
"is_interactive",
models.BooleanField(
default=False, help_text="Whether riders can interact with elements (shooting, etc.)"
),
),
(
"ride_system",
models.CharField(
blank=True,
help_text="Type of ride system (Omnimover, Trackless, Classic Track, etc.)",
max_length=100,
),
),
(
"uses_practical_effects",
models.BooleanField(default=True, help_text="Whether the ride uses practical/physical effects"),
),
(
"uses_motion_base",
models.BooleanField(default=False, help_text="Whether vehicles have motion simulation capability"),
),
(
"ride",
models.OneToOneField(
help_text="Ride these dark ride statistics belong to",
on_delete=django.db.models.deletion.CASCADE,
related_name="dark_stats",
to="rides.ride",
),
),
],
options={
"verbose_name": "Dark Ride Statistics",
"verbose_name_plural": "Dark Ride Statistics",
"ordering": ["ride"],
"abstract": False,
},
),
migrations.CreateModel(
name="DarkRideStatsEvent",
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)),
("scene_count", models.PositiveIntegerField(default=0, help_text="Number of themed scenes")),
(
"animatronic_count",
models.PositiveIntegerField(default=0, help_text="Number of animatronic figures"),
),
(
"has_projection_technology",
models.BooleanField(default=False, help_text="Whether the ride uses projection mapping or screens"),
),
(
"is_interactive",
models.BooleanField(
default=False, help_text="Whether riders can interact with elements (shooting, etc.)"
),
),
(
"ride_system",
models.CharField(
blank=True,
help_text="Type of ride system (Omnimover, Trackless, Classic Track, etc.)",
max_length=100,
),
),
(
"uses_practical_effects",
models.BooleanField(default=True, help_text="Whether the ride uses practical/physical effects"),
),
(
"uses_motion_base",
models.BooleanField(default=False, help_text="Whether vehicles have motion simulation capability"),
),
(
"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.darkridestats",
),
),
(
"ride",
models.ForeignKey(
db_constraint=False,
help_text="Ride these dark ride statistics belong to",
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="rides.ride",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="FlatRideStats",
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)),
(
"max_height_ft",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum ride height in feet", max_digits=6, null=True
),
),
(
"rotation_speed_rpm",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum rotation speed in RPM", max_digits=5, null=True
),
),
(
"swing_angle_degrees",
models.PositiveIntegerField(
blank=True, help_text="Maximum swing angle in degrees (for swinging rides)", null=True
),
),
(
"motion_type",
models.CharField(
choices=[
("SPINNING", "Spinning"),
("SWINGING", "Swinging"),
("BOUNCING", "Bouncing"),
("ROTATING", "Rotating"),
("DROPPING", "Dropping"),
("MIXED", "Mixed Motion"),
],
default="SPINNING",
help_text="Primary type of motion",
max_length=20,
),
),
("arm_count", models.PositiveIntegerField(blank=True, help_text="Number of arms/gondolas", null=True)),
(
"seats_per_gondola",
models.PositiveIntegerField(blank=True, help_text="Number of seats per gondola/arm", null=True),
),
(
"max_g_force",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum G-force experienced", max_digits=4, null=True
),
),
(
"ride",
models.OneToOneField(
help_text="Ride these flat ride statistics belong to",
on_delete=django.db.models.deletion.CASCADE,
related_name="flat_stats",
to="rides.ride",
),
),
],
options={
"verbose_name": "Flat Ride Statistics",
"verbose_name_plural": "Flat Ride Statistics",
"ordering": ["ride"],
"abstract": False,
},
),
migrations.CreateModel(
name="FlatRideStatsEvent",
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)),
(
"max_height_ft",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum ride height in feet", max_digits=6, null=True
),
),
(
"rotation_speed_rpm",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum rotation speed in RPM", max_digits=5, null=True
),
),
(
"swing_angle_degrees",
models.PositiveIntegerField(
blank=True, help_text="Maximum swing angle in degrees (for swinging rides)", null=True
),
),
(
"motion_type",
models.CharField(
choices=[
("SPINNING", "Spinning"),
("SWINGING", "Swinging"),
("BOUNCING", "Bouncing"),
("ROTATING", "Rotating"),
("DROPPING", "Dropping"),
("MIXED", "Mixed Motion"),
],
default="SPINNING",
help_text="Primary type of motion",
max_length=20,
),
),
("arm_count", models.PositiveIntegerField(blank=True, help_text="Number of arms/gondolas", null=True)),
(
"seats_per_gondola",
models.PositiveIntegerField(blank=True, help_text="Number of seats per gondola/arm", null=True),
),
(
"max_g_force",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum G-force experienced", max_digits=4, null=True
),
),
(
"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.flatridestats",
),
),
(
"ride",
models.ForeignKey(
db_constraint=False,
help_text="Ride these flat ride statistics belong to",
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="rides.ride",
),
),
],
options={
"abstract": False,
},
),
migrations.CreateModel(
name="WaterRideStats",
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)),
(
"wetness_level",
models.CharField(
choices=[
("DRY", "Dry - No water contact"),
("MILD", "Mild - Light misting"),
("MODERATE", "Moderate - Some splashing"),
("SOAKING", "Soaking - Prepare to get drenched"),
],
default="MODERATE",
help_text="How wet riders typically get",
max_length=10,
),
),
(
"splash_height_ft",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum splash height in feet", max_digits=5, null=True
),
),
(
"has_splash_zone",
models.BooleanField(
default=False, help_text="Whether there is a designated splash zone for spectators"
),
),
(
"boat_capacity",
models.PositiveIntegerField(blank=True, help_text="Number of riders per boat/raft", null=True),
),
(
"uses_flume",
models.BooleanField(default=False, help_text="Whether the ride uses a flume/log system"),
),
(
"rapids_sections",
models.PositiveIntegerField(default=0, help_text="Number of rapids/whitewater sections"),
),
(
"ride",
models.OneToOneField(
help_text="Ride these water statistics belong to",
on_delete=django.db.models.deletion.CASCADE,
related_name="water_stats",
to="rides.ride",
),
),
],
options={
"verbose_name": "Water Ride Statistics",
"verbose_name_plural": "Water Ride Statistics",
"ordering": ["ride"],
"abstract": False,
},
),
migrations.CreateModel(
name="WaterRideStatsEvent",
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)),
(
"wetness_level",
models.CharField(
choices=[
("DRY", "Dry - No water contact"),
("MILD", "Mild - Light misting"),
("MODERATE", "Moderate - Some splashing"),
("SOAKING", "Soaking - Prepare to get drenched"),
],
default="MODERATE",
help_text="How wet riders typically get",
max_length=10,
),
),
(
"splash_height_ft",
models.DecimalField(
blank=True, decimal_places=2, help_text="Maximum splash height in feet", max_digits=5, null=True
),
),
(
"has_splash_zone",
models.BooleanField(
default=False, help_text="Whether there is a designated splash zone for spectators"
),
),
(
"boat_capacity",
models.PositiveIntegerField(blank=True, help_text="Number of riders per boat/raft", null=True),
),
(
"uses_flume",
models.BooleanField(default=False, help_text="Whether the ride uses a flume/log system"),
),
(
"rapids_sections",
models.PositiveIntegerField(default=0, help_text="Number of rapids/whitewater sections"),
),
(
"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.waterridestats",
),
),
(
"ride",
models.ForeignKey(
db_constraint=False,
help_text="Ride these water statistics belong to",
on_delete=django.db.models.deletion.DO_NOTHING,
related_name="+",
related_query_name="+",
to="rides.ride",
),
),
],
options={
"abstract": False,
},
),
pgtrigger.migrations.AddTrigger(
model_name="darkridestats",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_darkridestatsevent" ("animatronic_count", "created_at", "has_projection_technology", "id", "is_interactive", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_id", "ride_system", "scene_count", "updated_at", "uses_motion_base", "uses_practical_effects") VALUES (NEW."animatronic_count", NEW."created_at", NEW."has_projection_technology", NEW."id", NEW."is_interactive", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_id", NEW."ride_system", NEW."scene_count", NEW."updated_at", NEW."uses_motion_base", NEW."uses_practical_effects"); RETURN NULL;',
hash="055ece465263a190b67dda7258c4bf26fa939107",
operation="INSERT",
pgid="pgtrigger_insert_insert_78d6d",
table="rides_darkridestats",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="darkridestats",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_darkridestatsevent" ("animatronic_count", "created_at", "has_projection_technology", "id", "is_interactive", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_id", "ride_system", "scene_count", "updated_at", "uses_motion_base", "uses_practical_effects") VALUES (NEW."animatronic_count", NEW."created_at", NEW."has_projection_technology", NEW."id", NEW."is_interactive", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_id", NEW."ride_system", NEW."scene_count", NEW."updated_at", NEW."uses_motion_base", NEW."uses_practical_effects"); RETURN NULL;',
hash="e7421fdd5dd3f044c5cc324db3e5d948e8f39afe",
operation="UPDATE",
pgid="pgtrigger_update_update_8eac1",
table="rides_darkridestats",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="flatridestats",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_flatridestatsevent" ("arm_count", "created_at", "id", "max_g_force", "max_height_ft", "motion_type", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_id", "rotation_speed_rpm", "seats_per_gondola", "swing_angle_degrees", "updated_at") VALUES (NEW."arm_count", NEW."created_at", NEW."id", NEW."max_g_force", NEW."max_height_ft", NEW."motion_type", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_id", NEW."rotation_speed_rpm", NEW."seats_per_gondola", NEW."swing_angle_degrees", NEW."updated_at"); RETURN NULL;',
hash="04edef9ce6235f57c5b53f052a7fe392835f2971",
operation="INSERT",
pgid="pgtrigger_insert_insert_a589a",
table="rides_flatridestats",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="flatridestats",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_flatridestatsevent" ("arm_count", "created_at", "id", "max_g_force", "max_height_ft", "motion_type", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_id", "rotation_speed_rpm", "seats_per_gondola", "swing_angle_degrees", "updated_at") VALUES (NEW."arm_count", NEW."created_at", NEW."id", NEW."max_g_force", NEW."max_height_ft", NEW."motion_type", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_id", NEW."rotation_speed_rpm", NEW."seats_per_gondola", NEW."swing_angle_degrees", NEW."updated_at"); RETURN NULL;',
hash="6f48930fae214744ad60cf8755ee0d50897d1040",
operation="UPDATE",
pgid="pgtrigger_update_update_f949f",
table="rides_flatridestats",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="waterridestats",
trigger=pgtrigger.compiler.Trigger(
name="insert_insert",
sql=pgtrigger.compiler.UpsertTriggerSql(
func='INSERT INTO "rides_waterridestatsevent" ("boat_capacity", "created_at", "has_splash_zone", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rapids_sections", "ride_id", "splash_height_ft", "updated_at", "uses_flume", "wetness_level") VALUES (NEW."boat_capacity", NEW."created_at", NEW."has_splash_zone", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rapids_sections", NEW."ride_id", NEW."splash_height_ft", NEW."updated_at", NEW."uses_flume", NEW."wetness_level"); RETURN NULL;',
hash="bb61031dc606f90bf9970f76417b30a72d8469ce",
operation="INSERT",
pgid="pgtrigger_insert_insert_fb731",
table="rides_waterridestats",
when="AFTER",
),
),
),
pgtrigger.migrations.AddTrigger(
model_name="waterridestats",
trigger=pgtrigger.compiler.Trigger(
name="update_update",
sql=pgtrigger.compiler.UpsertTriggerSql(
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
func='INSERT INTO "rides_waterridestatsevent" ("boat_capacity", "created_at", "has_splash_zone", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rapids_sections", "ride_id", "splash_height_ft", "updated_at", "uses_flume", "wetness_level") VALUES (NEW."boat_capacity", NEW."created_at", NEW."has_splash_zone", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rapids_sections", NEW."ride_id", NEW."splash_height_ft", NEW."updated_at", NEW."uses_flume", NEW."wetness_level"); RETURN NULL;',
hash="98404ec47c9b8d577f0a408a1182f9adffda7784",
operation="UPDATE",
pgid="pgtrigger_update_update_4f7a3",
table="rides_waterridestats",
when="AFTER",
),
),
),
]