diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index b8aaa751..0453cbe5 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -1,9 +1,11 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.contrib.auth.models import django.contrib.auth.validators import django.db.models.deletion import django.utils.timezone +import pgtrigger.compiler +import pgtrigger.migrations from django.conf import settings from django.db import migrations, models @@ -15,6 +17,7 @@ class Migration(migrations.Migration): dependencies = [ ("auth", "0012_alter_user_first_name_max_length"), ("contenttypes", "0002_remove_content_type_name"), + ("pghistory", "0006_delete_aggregateevent"), ] operations = [ @@ -229,15 +232,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name="TopList", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("title", models.CharField(max_length=100)), ( "category", @@ -268,6 +263,145 @@ class Migration(migrations.Migration): "ordering": ["-updated_at"], }, ), + migrations.CreateModel( + name="TopListEvent", + 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()), + ("title", models.CharField(max_length=100)), + ( + "category", + models.CharField( + choices=[ + ("RC", "Roller Coaster"), + ("DR", "Dark Ride"), + ("FR", "Flat Ride"), + ("WR", "Water Ride"), + ("PK", "Park"), + ], + max_length=2, + ), + ), + ("description", models.TextField(blank=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=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="accounts.toplist", + ), + ), + ( + "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, + }, + ), + migrations.CreateModel( + name="TopListItem", + fields=[ + ("id", models.BigAutoField(primary_key=True, serialize=False)), + ("object_id", models.PositiveIntegerField()), + ("rank", models.PositiveIntegerField()), + ("notes", models.TextField(blank=True)), + ( + "content_type", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + to="contenttypes.contenttype", + ), + ), + ( + "top_list", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="items", + to="accounts.toplist", + ), + ), + ], + options={ + "ordering": ["rank"], + }, + ), + migrations.CreateModel( + name="TopListItemEvent", + 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()), + ("object_id", models.PositiveIntegerField()), + ("rank", models.PositiveIntegerField()), + ("notes", models.TextField(blank=True)), + ( + "content_type", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "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="accounts.toplistitem", + ), + ), + ( + "top_list", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="accounts.toplist", + ), + ), + ], + options={ + "abstract": False, + }, + ), migrations.CreateModel( name="UserProfile", fields=[ @@ -318,40 +452,66 @@ class Migration(migrations.Migration): ), ], ), - migrations.CreateModel( - name="TopListItem", - fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), + pgtrigger.migrations.AddTrigger( + model_name="toplist", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "accounts_toplistevent" ("category", "created_at", "description", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "title", "updated_at", "user_id") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."title", NEW."updated_at", NEW."user_id"); RETURN NULL;', + hash="0b9e68b3aa0d3fb8f50bd832b99b70201d44aa11", + operation="INSERT", + pgid="pgtrigger_insert_insert_26546", + table="accounts_toplist", + when="AFTER", ), - ("object_id", models.PositiveIntegerField()), - ("rank", models.PositiveIntegerField()), - ("notes", models.TextField(blank=True)), - ( - "content_type", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - to="contenttypes.contenttype", - ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="toplist", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "accounts_toplistevent" ("category", "created_at", "description", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "title", "updated_at", "user_id") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."title", NEW."updated_at", NEW."user_id"); RETURN NULL;', + hash="3ae1293b8b1fe574bac9f388b60d19613347931e", + operation="UPDATE", + pgid="pgtrigger_update_update_84849", + table="accounts_toplist", + when="AFTER", ), - ( - "top_list", - models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, - related_name="items", - to="accounts.toplist", - ), + ), + ), + migrations.AlterUniqueTogether( + name="toplistitem", + unique_together={("top_list", "rank")}, + ), + pgtrigger.migrations.AddTrigger( + model_name="toplistitem", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "accounts_toplistitemevent" ("content_type_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rank", "top_list_id") VALUES (NEW."content_type_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rank", NEW."top_list_id"); RETURN NULL;', + hash="2944fa7c533fff0605752d711257be91eb44d5e0", + operation="INSERT", + pgid="pgtrigger_insert_insert_56dfc", + table="accounts_toplistitem", + when="AFTER", ), - ], - options={ - "ordering": ["rank"], - "unique_together": {("top_list", "rank")}, - }, + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="toplistitem", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "accounts_toplistitemevent" ("content_type_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rank", "top_list_id") VALUES (NEW."content_type_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rank", NEW."top_list_id"); RETURN NULL;', + hash="323f8b22f3bc97faae1b40bc14f7d03e51d2bdc0", + operation="UPDATE", + pgid="pgtrigger_update_update_2b6e3", + table="accounts_toplistitem", + when="AFTER", + ), + ), ), ] diff --git a/accounts/migrations/0002_add_pghistory.py b/accounts/migrations/0002_add_pghistory.py deleted file mode 100644 index 9b47710d..00000000 --- a/accounts/migrations/0002_add_pghistory.py +++ /dev/null @@ -1,110 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 18:42 - -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 = [ - ("accounts", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="TopListEvent", - 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()), - ("title", models.CharField(max_length=100)), - ("category", models.CharField(max_length=2)), - ("description", models.TextField(blank=True)), - ("created_at", models.DateTimeField()), - ("updated_at", models.DateTimeField()), - ("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="accounts.toplist")), - ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="TopListItemEvent", - 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()), - ("object_id", models.PositiveIntegerField()), - ("rank", models.PositiveIntegerField()), - ("notes", models.TextField(blank=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="accounts.toplistitem")), - ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), - ("top_list", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="accounts.toplist")), - ], - options={ - "abstract": False, - }, - ), - pgtrigger.migrations.AddTrigger( - model_name="toplist", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "accounts_toplistevent" ("category", "created_at", "description", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "title", "updated_at", "user_id") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."title", NEW."updated_at", NEW."user_id"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_toplist", - table="accounts_toplist", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="toplist", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "accounts_toplistevent" ("category", "created_at", "description", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "title", "updated_at", "user_id") VALUES (NEW."category", NEW."created_at", NEW."description", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."title", NEW."updated_at", NEW."user_id"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_toplist", - table="accounts_toplist", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="toplistitem", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "accounts_toplistitemevent" ("content_type_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rank", "top_list_id") VALUES (NEW."content_type_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rank", NEW."top_list_id"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_toplistitem", - table="accounts_toplistitem", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="toplistitem", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "accounts_toplistitemevent" ("content_type_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rank", "top_list_id") VALUES (NEW."content_type_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rank", NEW."top_list_id"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_toplistitem", - table="accounts_toplistitem", - when="AFTER", - ), - ), - ), - ] \ No newline at end of file diff --git a/analytics/migrations/0001_initial.py b/analytics/migrations/0001_initial.py index d3ef4548..a049b4f5 100644 --- a/analytics/migrations/0001_initial.py +++ b/analytics/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion from django.db import migrations, models diff --git a/companies/migrations/0001_initial.py b/companies/migrations/0001_initial.py index 1edf10a1..273be47b 100644 --- a/companies/migrations/0001_initial.py +++ b/companies/migrations/0001_initial.py @@ -1,5 +1,8 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations from django.db import migrations, models @@ -7,21 +10,15 @@ class Migration(migrations.Migration): initial = True - dependencies = [] + dependencies = [ + ("pghistory", "0006_delete_aggregateevent"), + ] operations = [ migrations.CreateModel( name="Company", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(max_length=255, unique=True)), ("website", models.URLField(blank=True)), @@ -37,18 +34,31 @@ class Migration(migrations.Migration): "ordering": ["name"], }, ), + 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()), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(db_index=False, max_length=255)), + ("website", models.URLField(blank=True)), + ("headquarters", models.CharField(blank=True, max_length=255)), + ("description", models.TextField(blank=True)), + ("total_parks", models.IntegerField(default=0)), + ("total_rides", models.IntegerField(default=0)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ], + options={ + "abstract": False, + }, + ), migrations.CreateModel( name="Manufacturer", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(max_length=255, unique=True)), ("website", models.URLField(blank=True)), @@ -63,4 +73,125 @@ class Migration(migrations.Migration): "ordering": ["name"], }, ), + migrations.CreateModel( + name="ManufacturerEvent", + 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)), + ("website", models.URLField(blank=True)), + ("headquarters", models.CharField(blank=True, max_length=255)), + ("description", models.TextField(blank=True)), + ("total_rides", models.IntegerField(default=0)), + ("total_roller_coasters", models.IntegerField(default=0)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="company", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="413671b13a748fb5f1acd57e8ec4af12ad7ae215", + operation="INSERT", + pgid="pgtrigger_insert_insert_a4101", + table="companies_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 "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="ee3eff1c96e46769347b8463d527668b7ece63c4", + operation="UPDATE", + pgid="pgtrigger_update_update_3d5ae", + table="companies_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="companies.company", + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="manufacturer", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="ac3c4c31aa8dffe569154454a6c4479d189c0f64", + operation="INSERT", + pgid="pgtrigger_insert_insert_5c0b6", + table="companies_manufacturer", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="manufacturer", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="c46f36f5811cd843ff61eab3ae77624ae2e69f60", + operation="UPDATE", + pgid="pgtrigger_update_update_81971", + table="companies_manufacturer", + when="AFTER", + ), + ), + ), + migrations.AddField( + model_name="manufacturerevent", + 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="manufacturerevent", + name="pgh_obj", + field=models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="events", + to="companies.manufacturer", + ), + ), ] diff --git a/companies/migrations/0002_add_designer_model.py b/companies/migrations/0002_add_designer_model.py deleted file mode 100644 index 4663df28..00000000 --- a/companies/migrations/0002_add_designer_model.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('companies', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='Designer', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255)), - ('slug', models.SlugField(max_length=255, unique=True)), - ('website', models.URLField(blank=True)), - ('description', models.TextField(blank=True)), - ('total_rides', models.IntegerField(default=0)), - ('total_roller_coasters', models.IntegerField(default=0)), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ], - options={ - 'ordering': ['name'], - }, - ), - ] diff --git a/companies/migrations/0003_companyevent_manufacturerevent.py b/companies/migrations/0003_companyevent_manufacturerevent.py deleted file mode 100644 index e0340447..00000000 --- a/companies/migrations/0003_companyevent_manufacturerevent.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 15:31 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0002_add_designer_model"), - ] - - 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()), - ("name", models.CharField(max_length=255)), - ("slug", models.SlugField(db_index=False, max_length=255)), - ("website", models.URLField(blank=True)), - ("headquarters", models.CharField(blank=True, max_length=255)), - ("description", models.TextField(blank=True)), - ("total_parks", models.IntegerField(default=0)), - ("total_rides", models.IntegerField(default=0)), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="ManufacturerEvent", - 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)), - ("website", models.URLField(blank=True)), - ("headquarters", models.CharField(blank=True, max_length=255)), - ("description", models.TextField(blank=True)), - ("total_rides", models.IntegerField(default=0)), - ("total_roller_coasters", models.IntegerField(default=0)), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - ] diff --git a/companies/migrations/0004_add_pghistory_triggers.py b/companies/migrations/0004_add_pghistory_triggers.py deleted file mode 100644 index f8000abb..00000000 --- a/companies/migrations/0004_add_pghistory_triggers.py +++ /dev/null @@ -1,68 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 17:51 - -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ("companies", "0003_companyevent_manufacturerevent"), - ] - - operations = [ - pgtrigger.migrations.AddTrigger( - model_name="company", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_company", - table="companies_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 "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_company", - table="companies_company", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="manufacturer", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_manufacturer", - table="companies_manufacturer", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="manufacturer", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_manufacturer", - table="companies_manufacturer", - when="AFTER", - ), - ), - ), - ] \ No newline at end of file diff --git a/companies/migrations/0004_delete_designer_alter_company_id_and_more.py b/companies/migrations/0004_delete_designer_alter_company_id_and_more.py deleted file mode 100644 index e9210352..00000000 --- a/companies/migrations/0004_delete_designer_alter_company_id_and_more.py +++ /dev/null @@ -1,130 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 15:31 - -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0003_companyevent_manufacturerevent"), - ("pghistory", "0006_delete_aggregateevent"), - ] - - operations = [ - migrations.DeleteModel( - name="Designer", - ), - migrations.AlterField( - model_name="company", - name="id", - field=models.BigAutoField(primary_key=True, serialize=False), - ), - migrations.AlterField( - model_name="manufacturer", - name="id", - field=models.BigAutoField(primary_key=True, serialize=False), - ), - pgtrigger.migrations.AddTrigger( - model_name="company", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="413671b13a748fb5f1acd57e8ec4af12ad7ae215", - operation="INSERT", - pgid="pgtrigger_insert_insert_a4101", - table="companies_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 "companies_companyevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_parks", "total_rides", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_parks", NEW."total_rides", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="ee3eff1c96e46769347b8463d527668b7ece63c4", - operation="UPDATE", - pgid="pgtrigger_update_update_3d5ae", - table="companies_company", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="manufacturer", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="ac3c4c31aa8dffe569154454a6c4479d189c0f64", - operation="INSERT", - pgid="pgtrigger_insert_insert_5c0b6", - table="companies_manufacturer", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="manufacturer", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "companies_manufacturerevent" ("created_at", "description", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "total_rides", "total_roller_coasters", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."total_rides", NEW."total_roller_coasters", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="c46f36f5811cd843ff61eab3ae77624ae2e69f60", - operation="UPDATE", - pgid="pgtrigger_update_update_81971", - table="companies_manufacturer", - 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="companies.company", - ), - ), - migrations.AddField( - model_name="manufacturerevent", - 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="manufacturerevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="companies.manufacturer", - ), - ), - ] diff --git a/companies/migrations/0005_merge_20250209_1447.py b/companies/migrations/0005_merge_20250209_1447.py deleted file mode 100644 index d724a8b7..00000000 --- a/companies/migrations/0005_merge_20250209_1447.py +++ /dev/null @@ -1,13 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 19:47 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0004_add_pghistory_triggers"), - ("companies", "0004_delete_designer_alter_company_id_and_more"), - ] - - operations = [] diff --git a/core/migrations/0001_initial.py b/core/migrations/0001_initial.py index 7316b71e..d5861f86 100644 --- a/core/migrations/0001_initial.py +++ b/core/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion from django.db import migrations, models diff --git a/designers/migrations/0001_initial.py b/designers/migrations/0001_initial.py index 8a48a2c7..b73112f8 100644 --- a/designers/migrations/0001_initial.py +++ b/designers/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion -import simple_history.models -from django.conf import settings +import pgtrigger.compiler +import pgtrigger.migrations from django.db import migrations, models @@ -11,22 +11,14 @@ class Migration(migrations.Migration): initial = True dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("pghistory", "0006_delete_aggregateevent"), ] operations = [ migrations.CreateModel( name="Designer", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("name", models.CharField(max_length=255)), ("slug", models.SlugField(max_length=255, unique=True)), ("description", models.TextField(blank=True)), @@ -41,48 +33,73 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name="HistoricalDesigner", + name="DesignerEvent", fields=[ - ( - "id", - models.BigIntegerField( - auto_created=True, blank=True, db_index=True, verbose_name="ID" - ), - ), + ("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(max_length=255)), + ("slug", models.SlugField(db_index=False, max_length=255)), ("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)), - ("created_at", models.DateTimeField(blank=True, editable=False)), - ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), ], options={ - "verbose_name": "historical designer", - "verbose_name_plural": "historical designers", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), + "abstract": False, }, - bases=(simple_history.models.HistoricalChanges, models.Model), + ), + pgtrigger.migrations.AddTrigger( + model_name="designer", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "designers_designerevent" ("created_at", "description", "founded_date", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."founded_date", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="876eaa3e1c7cf234f03cc706fa4e5e508ed780db", + operation="INSERT", + pgid="pgtrigger_insert_insert_9be65", + table="designers_designer", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="designer", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "designers_designerevent" ("created_at", "description", "founded_date", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."founded_date", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="edb092b6a122ca5827740a9afcdc6a885fe69c1c", + operation="UPDATE", + pgid="pgtrigger_update_update_b5f91", + table="designers_designer", + when="AFTER", + ), + ), + ), + migrations.AddField( + model_name="designerevent", + 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="designerevent", + name="pgh_obj", + field=models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="events", + to="designers.designer", + ), ), ] diff --git a/designers/migrations/0002_designerevent_remove_historicaldesigner_history_user_and_more.py b/designers/migrations/0002_designerevent_remove_historicaldesigner_history_user_and_more.py deleted file mode 100644 index 5a20d891..00000000 --- a/designers/migrations/0002_designerevent_remove_historicaldesigner_history_user_and_more.py +++ /dev/null @@ -1,99 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 15:24 - -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"), - ("pghistory", "0006_delete_aggregateevent"), - ] - - operations = [ - migrations.CreateModel( - name="DesignerEvent", - 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)), - ("website", models.URLField(blank=True)), - ("founded_date", models.DateField(blank=True, null=True)), - ("headquarters", models.CharField(blank=True, max_length=255)), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.RemoveField( - model_name="historicaldesigner", - name="history_user", - ), - migrations.AlterField( - model_name="designer", - name="id", - field=models.BigAutoField(primary_key=True, serialize=False), - ), - pgtrigger.migrations.AddTrigger( - model_name="designer", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "designers_designerevent" ("created_at", "description", "founded_date", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."founded_date", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="876eaa3e1c7cf234f03cc706fa4e5e508ed780db", - operation="INSERT", - pgid="pgtrigger_insert_insert_9be65", - table="designers_designer", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="designer", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "designers_designerevent" ("created_at", "description", "founded_date", "headquarters", "id", "name", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at", "website") VALUES (NEW."created_at", NEW."description", NEW."founded_date", NEW."headquarters", NEW."id", NEW."name", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="edb092b6a122ca5827740a9afcdc6a885fe69c1c", - operation="UPDATE", - pgid="pgtrigger_update_update_b5f91", - table="designers_designer", - when="AFTER", - ), - ), - ), - migrations.AddField( - model_name="designerevent", - 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="designerevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="designers.designer", - ), - ), - migrations.DeleteModel( - name="HistoricalDesigner", - ), - ] diff --git a/email_service/migrations/0001_initial.py b/email_service/migrations/0001_initial.py index 30ff9383..8c32c2b8 100644 --- a/email_service/migrations/0001_initial.py +++ b/email_service/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations from django.db import migrations, models @@ -9,6 +11,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ + ("pghistory", "0006_delete_aggregateevent"), ("sites", "0002_alter_domain_unique"), ] @@ -16,15 +19,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name="EmailConfiguration", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("api_key", models.CharField(max_length=255)), ("from_email", models.EmailField(max_length=254)), ( @@ -49,4 +44,86 @@ class Migration(migrations.Migration): "verbose_name_plural": "Email Configurations", }, ), + migrations.CreateModel( + name="EmailConfigurationEvent", + 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()), + ("api_key", models.CharField(max_length=255)), + ("from_email", models.EmailField(max_length=254)), + ( + "from_name", + models.CharField( + help_text="The name that will appear in the From field of emails", + max_length=255, + ), + ), + ("reply_to", models.EmailField(max_length=254)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=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="email_service.emailconfiguration", + ), + ), + ( + "site", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="sites.site", + ), + ), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="emailconfiguration", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "email_service_emailconfigurationevent" ("api_key", "created_at", "from_email", "from_name", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reply_to", "site_id", "updated_at") VALUES (NEW."api_key", NEW."created_at", NEW."from_email", NEW."from_name", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reply_to", NEW."site_id", NEW."updated_at"); RETURN NULL;', + hash="f19f3c7f7d904d5f850a2ff1e0bf1312e855c8c0", + operation="INSERT", + pgid="pgtrigger_insert_insert_08c59", + table="email_service_emailconfiguration", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="emailconfiguration", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "email_service_emailconfigurationevent" ("api_key", "created_at", "from_email", "from_name", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reply_to", "site_id", "updated_at") VALUES (NEW."api_key", NEW."created_at", NEW."from_email", NEW."from_name", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reply_to", NEW."site_id", NEW."updated_at"); RETURN NULL;', + hash="e445521baf2cfb51379b2a6be550b4a638d60202", + operation="UPDATE", + pgid="pgtrigger_update_update_992a4", + table="email_service_emailconfiguration", + when="AFTER", + ), + ), + ), ] diff --git a/email_service/migrations/0002_add_pghistory.py b/email_service/migrations/0002_add_pghistory.py deleted file mode 100644 index 5b160748..00000000 --- a/email_service/migrations/0002_add_pghistory.py +++ /dev/null @@ -1,63 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 18:31 - -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - -class Migration(migrations.Migration): - dependencies = [ - ("email_service", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - ] - - operations = [ - migrations.CreateModel( - name="EmailConfigurationEvent", - 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()), - ("api_key", models.CharField(max_length=255)), - ("from_email", models.EmailField(max_length=254)), - ("from_name", models.CharField(max_length=255)), - ("reply_to", models.EmailField(max_length=254)), - ("created_at", models.DateTimeField()), - ("updated_at", models.DateTimeField()), - ("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="email_service.emailconfiguration")), - ("site", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="django.contrib.sites.models.Site")), - ], - options={ - "abstract": False, - }, - ), - pgtrigger.migrations.AddTrigger( - model_name="emailconfiguration", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "email_service_emailconfigurationevent" ("api_key", "created_at", "from_email", "from_name", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reply_to", "site_id", "updated_at") VALUES (NEW."api_key", NEW."created_at", NEW."from_email", NEW."from_name", NEW."id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reply_to", NEW."site_id", NEW."updated_at"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_emailconfig", - table="email_service_emailconfiguration", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="emailconfiguration", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "email_service_emailconfigurationevent" ("api_key", "created_at", "from_email", "from_name", "id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reply_to", "site_id", "updated_at") VALUES (NEW."api_key", NEW."created_at", NEW."from_email", NEW."from_name", NEW."id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reply_to", NEW."site_id", NEW."updated_at"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_emailconfig", - table="email_service_emailconfiguration", - when="AFTER", - ), - ), - ), - ] \ No newline at end of file diff --git a/history_tracking/migrations/0001_initial.py b/history_tracking/migrations/0001_initial.py index afb92d14..7b299786 100644 --- a/history_tracking/migrations/0001_initial.py +++ b/history_tracking/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion from django.db import migrations, models diff --git a/history_tracking/mixins.py b/history_tracking/mixins.py deleted file mode 100644 index 1ddf4347..00000000 --- a/history_tracking/mixins.py +++ /dev/null @@ -1,74 +0,0 @@ -# history_tracking/mixins.py -from django.db import models -from django.conf import settings - -class HistoricalChangeMixin(models.Model): - """Mixin for historical models to track changes""" - id = models.BigIntegerField(db_index=True, auto_created=True, blank=True) - history_date = models.DateTimeField() - history_id = models.AutoField(primary_key=True) - history_type = models.CharField(max_length=1) - history_user = models.ForeignKey( - settings.AUTH_USER_MODEL, - null=True, - on_delete=models.SET_NULL, - related_name='+' - ) - history_change_reason = models.CharField(max_length=100, null=True) - - class Meta: - abstract = True - ordering = ['-history_date', '-history_id'] - - @property - def prev_record(self): - """Get the previous record for this instance""" - try: - return self.__class__.objects.filter( - history_date__lt=self.history_date, - id=self.id - ).order_by('-history_date').first() - except (AttributeError, TypeError): - return None - - @property - def diff_against_previous(self): - prev_record = self.prev_record - if not prev_record: - return {} - - changes = {} - for field in self.__dict__: - if field not in [ - "history_date", - "history_id", - "history_type", - "history_user_id", - "history_change_reason", - "history_type", - "id", - "_state", - "_history_user_cache" - ] and not field.startswith("_"): - try: - old_value = getattr(prev_record, field) - new_value = getattr(self, field) - if old_value != new_value: - changes[field] = {"old": str(old_value), "new": str(new_value)} - except AttributeError: - continue - return changes - - @property - def history_user_display(self): - """Get a display name for the history user""" - if hasattr(self, 'history_user') and self.history_user: - return str(self.history_user) - return None - - def get_instance(self): - """Get the model instance this history record represents""" - try: - return self.__class__.objects.get(id=self.id) - except self.__class__.DoesNotExist: - return None diff --git a/history_tracking/models.py b/history_tracking/models.py index 985dce4d..b125fb7d 100644 --- a/history_tracking/models.py +++ b/history_tracking/models.py @@ -1,18 +1,14 @@ -# history_tracking/models.py from django.db import models from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.fields import GenericForeignKey -from django.contrib.auth import get_user_model -from simple_history.models import HistoricalRecords -from .mixins import HistoricalChangeMixin -from typing import Any, Type, TypeVar, cast +from django.conf import settings +from typing import Any, Dict, Optional from django.db.models import QuerySet -T = TypeVar('T', bound=models.Model) - class DiffMixin: - """Mixin to add diffing capabilities to pghistory events""" - def get_prev_record(self): + """Mixin to add diffing capabilities to models""" + + def get_prev_record(self) -> Optional[Any]: """Get the previous record for this instance""" try: return type(self).objects.filter( @@ -22,73 +18,53 @@ class DiffMixin: except (AttributeError, TypeError): return None - def diff_against_previous(self): + def diff_against_previous(self) -> Dict: """Compare this record against the previous one""" prev_record = self.get_prev_record() if not prev_record: return {} - changes = {} skip_fields = { 'pgh_id', 'pgh_created_at', 'pgh_label', - 'pgh_obj_id', 'pgh_context_id', '_state' + 'pgh_obj_id', 'pgh_context_id', '_state', + 'created_at', 'updated_at' } - for field in self.__dict__: - if field not in skip_fields and not field.startswith('_'): - try: - old_value = getattr(prev_record, field) - new_value = getattr(self, field) - if old_value != new_value: - changes[field] = {"old": str(old_value), "new": str(new_value)} - except AttributeError: - continue + changes = {} + for field, value in self.__dict__.items(): + # Skip internal fields and those we don't want to track + if field.startswith('_') or field in skip_fields or field.endswith('_id'): + continue + + try: + old_value = getattr(prev_record, field) + new_value = value + if old_value != new_value: + changes[field] = { + "old": str(old_value) if old_value is not None else "None", + "new": str(new_value) if new_value is not None else "None" + } + except AttributeError: + continue + return changes -class HistoricalModel(models.Model): - """ - Legacy abstract base class for models with history tracking. - Use TrackedModel for new implementations. - """ - id = models.BigAutoField(primary_key=True) - history: HistoricalRecords = HistoricalRecords( - inherit=True, - bases=(HistoricalChangeMixin,) - ) - - class Meta: - abstract = True - - @property - def _history_model(self) -> Type[T]: - """Get the history model class""" - return cast(Type[T], self.history.model) # type: ignore - - def get_history(self) -> QuerySet: - """Get all history records for this instance""" - model = self._history_model - return model.objects.filter(id=self.pk).order_by('-history_date') - class TrackedModel(models.Model): - """Abstract base class for models with pghistory tracking. - The @pghistory.track() decorator should be applied to concrete models - that inherit from this class. - """ - id = models.BigAutoField(primary_key=True) + """Abstract base class for models that need history tracking""" + created_at = models.DateTimeField(auto_now_add=True) + updated_at = models.DateTimeField(auto_now=True) class Meta: abstract = True def get_history(self) -> QuerySet: """Get all history records for this instance in chronological order""" - history_model = self.get_history_model() - return history_model.objects.filter( - pgh_obj_id=self.pk - ).order_by('-pgh_created_at') - - def get_history_model(self) -> Type[Any]: - """Get the pghistory model for this instance""" - return self.pgh_obj_event_model + event_model = self.events.model # pghistory provides this automatically + if event_model: + return event_model.objects.filter( + pgh_obj_id=self.pk + ).order_by('-pgh_created_at') + return self.__class__.objects.none() class HistoricalSlug(models.Model): """Track historical slugs for models""" @@ -97,6 +73,13 @@ class HistoricalSlug(models.Model): content_object = GenericForeignKey('content_type', 'object_id') slug = models.SlugField(max_length=255) created_at = models.DateTimeField(auto_now_add=True) + user = models.ForeignKey( + settings.AUTH_USER_MODEL, + null=True, + blank=True, + on_delete=models.SET_NULL, + related_name='historical_slugs' + ) class Meta: unique_together = ('content_type', 'slug') diff --git a/location/migrations/0001_initial.py b/location/migrations/0001_initial.py index de40b309..98556257 100644 --- a/location/migrations/0001_initial.py +++ b/location/migrations/0001_initial.py @@ -1,10 +1,10 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.contrib.gis.db.models.fields import django.core.validators import django.db.models.deletion -import simple_history.models -from django.conf import settings +import pgtrigger.compiler +import pgtrigger.migrations from django.db import migrations, models @@ -14,140 +14,14 @@ class Migration(migrations.Migration): dependencies = [ ("contenttypes", "0002_remove_content_type_name"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("pghistory", "0006_delete_aggregateevent"), ] operations = [ - migrations.CreateModel( - name="HistoricalLocation", - fields=[ - ( - "id", - models.BigIntegerField( - auto_created=True, blank=True, db_index=True, verbose_name="ID" - ), - ), - ("object_id", models.PositiveIntegerField()), - ( - "name", - models.CharField( - help_text="Name of the location (e.g. business name, landmark)", - max_length=255, - ), - ), - ( - "location_type", - models.CharField( - help_text="Type of location (e.g. business, landmark, address)", - max_length=50, - ), - ), - ( - "latitude", - models.DecimalField( - blank=True, - decimal_places=6, - help_text="Latitude coordinate (legacy field)", - max_digits=9, - null=True, - validators=[ - django.core.validators.MinValueValidator(-90), - django.core.validators.MaxValueValidator(90), - ], - ), - ), - ( - "longitude", - models.DecimalField( - blank=True, - decimal_places=6, - help_text="Longitude coordinate (legacy field)", - max_digits=9, - null=True, - validators=[ - django.core.validators.MinValueValidator(-180), - django.core.validators.MaxValueValidator(180), - ], - ), - ), - ( - "point", - django.contrib.gis.db.models.fields.PointField( - blank=True, - help_text="Geographic coordinates as a Point", - null=True, - srid=4326, - ), - ), - ( - "street_address", - models.CharField(blank=True, max_length=255, null=True), - ), - ("city", models.CharField(blank=True, max_length=100, null=True)), - ( - "state", - models.CharField( - blank=True, - help_text="State/Region/Province", - max_length=100, - null=True, - ), - ), - ("country", models.CharField(blank=True, max_length=100, null=True)), - ("postal_code", models.CharField(blank=True, max_length=20, null=True)), - ("created_at", models.DateTimeField(blank=True, editable=False)), - ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "content_type", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="contenttypes.contenttype", - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ], - options={ - "verbose_name": "historical location", - "verbose_name_plural": "historical locations", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), migrations.CreateModel( name="Location", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("object_id", models.PositiveIntegerField()), ( "name", @@ -228,16 +102,163 @@ class Migration(migrations.Migration): ], options={ "ordering": ["name"], - "indexes": [ - models.Index( - fields=["content_type", "object_id"], - name="location_lo_content_9ee1bd_idx", - ), - models.Index(fields=["city"], name="location_lo_city_99f908_idx"), - models.Index( - fields=["country"], name="location_lo_country_b75eba_idx" - ), - ], }, ), + migrations.CreateModel( + name="LocationEvent", + 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()), + ("object_id", models.PositiveIntegerField()), + ( + "name", + models.CharField( + help_text="Name of the location (e.g. business name, landmark)", + max_length=255, + ), + ), + ( + "location_type", + models.CharField( + help_text="Type of location (e.g. business, landmark, address)", + max_length=50, + ), + ), + ( + "latitude", + models.DecimalField( + blank=True, + decimal_places=6, + help_text="Latitude coordinate (legacy field)", + max_digits=9, + null=True, + validators=[ + django.core.validators.MinValueValidator(-90), + django.core.validators.MaxValueValidator(90), + ], + ), + ), + ( + "longitude", + models.DecimalField( + blank=True, + decimal_places=6, + help_text="Longitude coordinate (legacy field)", + max_digits=9, + null=True, + validators=[ + django.core.validators.MinValueValidator(-180), + django.core.validators.MaxValueValidator(180), + ], + ), + ), + ( + "point", + django.contrib.gis.db.models.fields.PointField( + blank=True, + help_text="Geographic coordinates as a Point", + null=True, + srid=4326, + ), + ), + ( + "street_address", + models.CharField(blank=True, max_length=255, null=True), + ), + ("city", models.CharField(blank=True, max_length=100, null=True)), + ( + "state", + models.CharField( + blank=True, + help_text="State/Region/Province", + max_length=100, + null=True, + ), + ), + ("country", models.CharField(blank=True, max_length=100, null=True)), + ("postal_code", models.CharField(blank=True, max_length=20, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "content_type", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "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="location.location", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.AddIndex( + model_name="location", + index=models.Index( + fields=["content_type", "object_id"], + name="location_lo_content_9ee1bd_idx", + ), + ), + migrations.AddIndex( + model_name="location", + index=models.Index(fields=["city"], name="location_lo_city_99f908_idx"), + ), + migrations.AddIndex( + model_name="location", + index=models.Index( + fields=["country"], name="location_lo_country_b75eba_idx" + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="location", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "location_locationevent" ("city", "content_type_id", "country", "created_at", "id", "latitude", "location_type", "longitude", "name", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "point", "postal_code", "state", "street_address", "updated_at") VALUES (NEW."city", NEW."content_type_id", NEW."country", NEW."created_at", NEW."id", NEW."latitude", NEW."location_type", NEW."longitude", NEW."name", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."point", NEW."postal_code", NEW."state", NEW."street_address", NEW."updated_at"); RETURN NULL;', + hash="8a8f00869cfcaa1a23ab29b3d855e83602172c67", + operation="INSERT", + pgid="pgtrigger_insert_insert_98cd4", + table="location_location", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="location", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "location_locationevent" ("city", "content_type_id", "country", "created_at", "id", "latitude", "location_type", "longitude", "name", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "point", "postal_code", "state", "street_address", "updated_at") VALUES (NEW."city", NEW."content_type_id", NEW."country", NEW."created_at", NEW."id", NEW."latitude", NEW."location_type", NEW."longitude", NEW."name", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."point", NEW."postal_code", NEW."state", NEW."street_address", NEW."updated_at"); RETURN NULL;', + hash="f3378cb26a5d88aa82c8fae016d46037b530de90", + operation="UPDATE", + pgid="pgtrigger_update_update_471d2", + table="location_location", + when="AFTER", + ), + ), + ), ] diff --git a/location/migrations/0002_locationevent_remove_historicallocation_content_type_and_more.py b/location/migrations/0002_locationevent_remove_historicallocation_content_type_and_more.py deleted file mode 100644 index b0a75bd4..00000000 --- a/location/migrations/0002_locationevent_remove_historicallocation_content_type_and_more.py +++ /dev/null @@ -1,179 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 16:13 - -import django.contrib.gis.db.models.fields -import django.core.validators -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("contenttypes", "0002_remove_content_type_name"), - ("location", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - ] - - operations = [ - migrations.CreateModel( - name="LocationEvent", - 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()), - ("object_id", models.PositiveIntegerField()), - ( - "name", - models.CharField( - help_text="Name of the location (e.g. business name, landmark)", - max_length=255, - ), - ), - ( - "location_type", - models.CharField( - help_text="Type of location (e.g. business, landmark, address)", - max_length=50, - ), - ), - ( - "latitude", - models.DecimalField( - blank=True, - decimal_places=6, - help_text="Latitude coordinate (legacy field)", - max_digits=9, - null=True, - validators=[ - django.core.validators.MinValueValidator(-90), - django.core.validators.MaxValueValidator(90), - ], - ), - ), - ( - "longitude", - models.DecimalField( - blank=True, - decimal_places=6, - help_text="Longitude coordinate (legacy field)", - max_digits=9, - null=True, - validators=[ - django.core.validators.MinValueValidator(-180), - django.core.validators.MaxValueValidator(180), - ], - ), - ), - ( - "point", - django.contrib.gis.db.models.fields.PointField( - blank=True, - help_text="Geographic coordinates as a Point", - null=True, - srid=4326, - ), - ), - ( - "street_address", - models.CharField(blank=True, max_length=255, null=True), - ), - ("city", models.CharField(blank=True, max_length=100, null=True)), - ( - "state", - models.CharField( - blank=True, - help_text="State/Region/Province", - max_length=100, - null=True, - ), - ), - ("country", models.CharField(blank=True, max_length=100, null=True)), - ("postal_code", models.CharField(blank=True, max_length=20, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.RemoveField( - model_name="historicallocation", - name="content_type", - ), - migrations.RemoveField( - model_name="historicallocation", - name="history_user", - ), - migrations.AlterField( - model_name="location", - name="id", - field=models.BigAutoField(primary_key=True, serialize=False), - ), - pgtrigger.migrations.AddTrigger( - model_name="location", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "location_locationevent" ("city", "content_type_id", "country", "created_at", "id", "latitude", "location_type", "longitude", "name", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "point", "postal_code", "state", "street_address", "updated_at") VALUES (NEW."city", NEW."content_type_id", NEW."country", NEW."created_at", NEW."id", NEW."latitude", NEW."location_type", NEW."longitude", NEW."name", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."point", NEW."postal_code", NEW."state", NEW."street_address", NEW."updated_at"); RETURN NULL;', - hash="8a8f00869cfcaa1a23ab29b3d855e83602172c67", - operation="INSERT", - pgid="pgtrigger_insert_insert_98cd4", - table="location_location", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="location", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "location_locationevent" ("city", "content_type_id", "country", "created_at", "id", "latitude", "location_type", "longitude", "name", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "point", "postal_code", "state", "street_address", "updated_at") VALUES (NEW."city", NEW."content_type_id", NEW."country", NEW."created_at", NEW."id", NEW."latitude", NEW."location_type", NEW."longitude", NEW."name", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."point", NEW."postal_code", NEW."state", NEW."street_address", NEW."updated_at"); RETURN NULL;', - hash="f3378cb26a5d88aa82c8fae016d46037b530de90", - operation="UPDATE", - pgid="pgtrigger_update_update_471d2", - table="location_location", - when="AFTER", - ), - ), - ), - migrations.AddField( - model_name="locationevent", - name="content_type", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="contenttypes.contenttype", - ), - ), - migrations.AddField( - model_name="locationevent", - 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="locationevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="location.location", - ), - ), - migrations.DeleteModel( - name="HistoricalLocation", - ), - ] diff --git a/media/migrations/0001_initial.py b/media/migrations/0001_initial.py index 418e1b30..728e1008 100644 --- a/media/migrations/0001_initial.py +++ b/media/migrations/0001_initial.py @@ -1,8 +1,10 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion import media.models import media.storage +import pgtrigger.compiler +import pgtrigger.migrations from django.conf import settings from django.db import migrations, models @@ -13,6 +15,7 @@ class Migration(migrations.Migration): dependencies = [ ("contenttypes", "0002_remove_content_type_name"), + ("pghistory", "0006_delete_aggregateevent"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -20,15 +23,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name="Photo", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ( "image", models.ImageField( @@ -64,12 +59,110 @@ class Migration(migrations.Migration): ], options={ "ordering": ["-is_primary", "-created_at"], - "indexes": [ - models.Index( - fields=["content_type", "object_id"], - name="media_photo_content_0187f5_idx", - ) - ], }, ), + migrations.CreateModel( + name="PhotoEvent", + 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()), + ( + "image", + models.ImageField( + max_length=255, + storage=media.storage.MediaStorage(), + upload_to=media.models.photo_upload_path, + ), + ), + ("caption", models.CharField(blank=True, max_length=255)), + ("alt_text", models.CharField(blank=True, max_length=255)), + ("is_primary", models.BooleanField(default=False)), + ("is_approved", models.BooleanField(default=False)), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ("date_taken", models.DateTimeField(blank=True, null=True)), + ("object_id", models.PositiveIntegerField()), + ( + "content_type", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "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="media.photo", + ), + ), + ( + "uploaded_by", + models.ForeignKey( + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.AddIndex( + model_name="photo", + index=models.Index( + fields=["content_type", "object_id"], + name="media_photo_content_0187f5_idx", + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="photo", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "media_photoevent" ("alt_text", "caption", "content_type_id", "created_at", "date_taken", "id", "image", "is_approved", "is_primary", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."id", NEW."image", NEW."is_approved", NEW."is_primary", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;', + hash="c75cf37b6fac8d5593598ba2af194f1f9a692838", + operation="INSERT", + pgid="pgtrigger_insert_insert_e1ca0", + table="media_photo", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="photo", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "media_photoevent" ("alt_text", "caption", "content_type_id", "created_at", "date_taken", "id", "image", "is_approved", "is_primary", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."id", NEW."image", NEW."is_approved", NEW."is_primary", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;', + hash="09d9b3bda4d950d7a7104c8f013a93d05025da72", + operation="UPDATE", + pgid="pgtrigger_update_update_6ff7d", + table="media_photo", + when="AFTER", + ), + ), + ), ] diff --git a/media/migrations/0002_add_pghistory.py b/media/migrations/0002_add_pghistory.py deleted file mode 100644 index 03c9e8a0..00000000 --- a/media/migrations/0002_add_pghistory.py +++ /dev/null @@ -1,69 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 18:01 - -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 = [ - ("media", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="PhotoEvent", - 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()), - ("image", models.ImageField(max_length=255, upload_to="events")), - ("caption", models.CharField(blank=True, max_length=255)), - ("alt_text", models.CharField(blank=True, max_length=255)), - ("is_primary", models.BooleanField(default=False)), - ("is_approved", models.BooleanField(default=False)), - ("created_at", models.DateTimeField()), - ("updated_at", models.DateTimeField()), - ("date_taken", models.DateTimeField(blank=True, null=True)), - ("object_id", models.PositiveIntegerField()), - ("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="media.photo")), - ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), - ("uploaded_by", models.ForeignKey(db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ], - options={ - "abstract": False, - }, - ), - pgtrigger.migrations.AddTrigger( - model_name="photo", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "media_photoevent" ("alt_text", "caption", "content_type_id", "created_at", "date_taken", "id", "image", "is_approved", "is_primary", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."id", NEW."image", NEW."is_approved", NEW."is_primary", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_photo", - table="media_photo", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="photo", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "media_photoevent" ("alt_text", "caption", "content_type_id", "created_at", "date_taken", "id", "image", "is_approved", "is_primary", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "updated_at", "uploaded_by_id") VALUES (NEW."alt_text", NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."id", NEW."image", NEW."is_approved", NEW."is_primary", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."updated_at", NEW."uploaded_by_id"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_photo", - table="media_photo", - when="AFTER", - ), - ), - ), - ] \ No newline at end of file diff --git a/moderation/migrations/0001_initial.py b/moderation/migrations/0001_initial.py index ea23b8f9..e4c6996f 100644 --- a/moderation/migrations/0001_initial.py +++ b/moderation/migrations/0001_initial.py @@ -1,6 +1,8 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations from django.conf import settings from django.db import migrations, models @@ -11,6 +13,7 @@ class Migration(migrations.Migration): dependencies = [ ("contenttypes", "0002_remove_content_type_name"), + ("pghistory", "0006_delete_aggregateevent"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -18,15 +21,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name="EditSubmission", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("object_id", models.PositiveIntegerField(blank=True, null=True)), ( "submission_type", @@ -42,6 +37,14 @@ class Migration(migrations.Migration): help_text="JSON representation of the changes or new object data" ), ), + ( + "moderator_changes", + models.JSONField( + blank=True, + help_text="Moderator's edited version of the changes before approval", + null=True, + ), + ), ( "reason", models.TextField(help_text="Why this edit/addition is needed"), @@ -56,12 +59,12 @@ class Migration(migrations.Migration): "status", models.CharField( choices=[ - ("NEW", "New"), + ("PENDING", "Pending"), ("APPROVED", "Approved"), ("REJECTED", "Rejected"), ("ESCALATED", "Escalated"), ], - default="NEW", + default="PENDING", max_length=20, ), ), @@ -102,29 +105,130 @@ class Migration(migrations.Migration): ], options={ "ordering": ["-created_at"], - "indexes": [ - models.Index( - fields=["content_type", "object_id"], - name="moderation__content_922d2b_idx", + }, + ), + migrations.CreateModel( + name="EditSubmissionEvent", + 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()), + ("object_id", models.PositiveIntegerField(blank=True, null=True)), + ( + "submission_type", + models.CharField( + choices=[("EDIT", "Edit Existing"), ("CREATE", "Create New")], + default="EDIT", + max_length=10, ), - models.Index( - fields=["status"], name="moderation__status_e4eb2b_idx" + ), + ( + "changes", + models.JSONField( + help_text="JSON representation of the changes or new object data" ), - ], + ), + ( + "moderator_changes", + models.JSONField( + blank=True, + help_text="Moderator's edited version of the changes before approval", + null=True, + ), + ), + ( + "reason", + models.TextField(help_text="Why this edit/addition is needed"), + ), + ( + "source", + models.TextField( + blank=True, help_text="Source of information (if applicable)" + ), + ), + ( + "status", + models.CharField( + choices=[ + ("PENDING", "Pending"), + ("APPROVED", "Approved"), + ("REJECTED", "Rejected"), + ("ESCALATED", "Escalated"), + ], + default="PENDING", + max_length=20, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("handled_at", models.DateTimeField(blank=True, null=True)), + ( + "notes", + models.TextField( + blank=True, + help_text="Notes from the moderator about this submission", + ), + ), + ( + "content_type", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "handled_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="moderation.editsubmission", + ), + ), + ( + "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, }, ), migrations.CreateModel( name="PhotoSubmission", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("object_id", models.PositiveIntegerField()), ("photo", models.ImageField(upload_to="submissions/photos/")), ("caption", models.CharField(blank=True, max_length=255)), @@ -133,12 +237,12 @@ class Migration(migrations.Migration): "status", models.CharField( choices=[ - ("NEW", "New"), + ("PENDING", "Pending"), ("APPROVED", "Approved"), ("REJECTED", "Rejected"), - ("AUTO_APPROVED", "Auto Approved"), + ("ESCALATED", "Escalated"), ], - default="NEW", + default="PENDING", max_length=20, ), ), @@ -179,15 +283,175 @@ class Migration(migrations.Migration): ], options={ "ordering": ["-created_at"], - "indexes": [ - models.Index( - fields=["content_type", "object_id"], - name="moderation__content_7a7bc1_idx", - ), - models.Index( - fields=["status"], name="moderation__status_7a1914_idx" - ), - ], }, ), + migrations.CreateModel( + name="PhotoSubmissionEvent", + 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()), + ("object_id", models.PositiveIntegerField()), + ("photo", models.ImageField(upload_to="submissions/photos/")), + ("caption", models.CharField(blank=True, max_length=255)), + ("date_taken", models.DateField(blank=True, null=True)), + ( + "status", + models.CharField( + choices=[ + ("PENDING", "Pending"), + ("APPROVED", "Approved"), + ("REJECTED", "Rejected"), + ("ESCALATED", "Escalated"), + ], + default="PENDING", + max_length=20, + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ("handled_at", models.DateTimeField(blank=True, null=True)), + ( + "notes", + models.TextField( + blank=True, + help_text="Notes from the moderator about this photo submission", + ), + ), + ( + "content_type", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "handled_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="moderation.photosubmission", + ), + ), + ( + "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, + }, + ), + migrations.AddIndex( + model_name="editsubmission", + index=models.Index( + fields=["content_type", "object_id"], + name="moderation__content_922d2b_idx", + ), + ), + migrations.AddIndex( + model_name="editsubmission", + index=models.Index(fields=["status"], name="moderation__status_e4eb2b_idx"), + ), + pgtrigger.migrations.AddTrigger( + model_name="editsubmission", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', + hash="616bbed667e6f8a1b23dfa39b5b3fd0b3bc0b43d", + operation="INSERT", + pgid="pgtrigger_insert_insert_2c796", + table="moderation_editsubmission", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="editsubmission", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', + hash="76c447d8cfeced3bb1893e2d900c97bb05a9f028", + operation="UPDATE", + pgid="pgtrigger_update_update_ab38f", + table="moderation_editsubmission", + when="AFTER", + ), + ), + ), + migrations.AddIndex( + model_name="photosubmission", + index=models.Index( + fields=["content_type", "object_id"], + name="moderation__content_7a7bc1_idx", + ), + ), + migrations.AddIndex( + model_name="photosubmission", + index=models.Index(fields=["status"], name="moderation__status_7a1914_idx"), + ), + pgtrigger.migrations.AddTrigger( + model_name="photosubmission", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', + hash="ea6563a26e5875de544fa270751df4f48003a4c0", + operation="INSERT", + pgid="pgtrigger_insert_insert_62865", + table="moderation_photosubmission", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="photosubmission", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', + hash="64f35eedbad17d4060eaeab7f2bd944620465591", + operation="UPDATE", + pgid="pgtrigger_update_update_9c311", + table="moderation_photosubmission", + when="AFTER", + ), + ), + ), ] diff --git a/moderation/migrations/0002_add_pghistory.py b/moderation/migrations/0002_add_pghistory.py deleted file mode 100644 index eda53e0f..00000000 --- a/moderation/migrations/0002_add_pghistory.py +++ /dev/null @@ -1,123 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 18:26 - -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 = [ - ("moderation", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="EditSubmissionEvent", - 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()), - ("object_id", models.PositiveIntegerField(blank=True, null=True)), - ("submission_type", models.CharField(max_length=10)), - ("changes", models.JSONField()), - ("moderator_changes", models.JSONField(blank=True, null=True)), - ("reason", models.TextField()), - ("source", models.TextField(blank=True)), - ("status", models.CharField(max_length=20)), - ("created_at", models.DateTimeField()), - ("handled_at", models.DateTimeField(blank=True, null=True)), - ("notes", models.TextField(blank=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="moderation.editsubmission")), - ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), - ("handled_by", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="PhotoSubmissionEvent", - 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()), - ("object_id", models.PositiveIntegerField()), - ("photo", models.ImageField(upload_to="events/photos/")), - ("caption", models.CharField(blank=True, max_length=255)), - ("date_taken", models.DateField(blank=True, null=True)), - ("status", models.CharField(max_length=20)), - ("created_at", models.DateTimeField()), - ("handled_at", models.DateTimeField(blank=True, null=True)), - ("notes", models.TextField(blank=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="moderation.photosubmission")), - ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), - ("handled_by", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ], - options={ - "abstract": False, - }, - ), - pgtrigger.migrations.AddTrigger( - model_name="editsubmission", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_editsubmission", - table="moderation_editsubmission", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="editsubmission", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_editsubmission", - table="moderation_editsubmission", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="photosubmission", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_photosubmission", - table="moderation_photosubmission", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="photosubmission", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_photosubmission", - table="moderation_photosubmission", - when="AFTER", - ), - ), - ), - ] \ No newline at end of file diff --git a/moderation/migrations/0002_alter_editsubmission_status_and_more.py b/moderation/migrations/0002_alter_editsubmission_status_and_more.py deleted file mode 100644 index 8e018fa2..00000000 --- a/moderation/migrations/0002_alter_editsubmission_status_and_more.py +++ /dev/null @@ -1,41 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-13 19:25 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("moderation", "0001_initial"), - ] - - operations = [ - migrations.AlterField( - model_name="editsubmission", - name="status", - field=models.CharField( - choices=[ - ("PENDING", "Pending"), - ("APPROVED", "Approved"), - ("REJECTED", "Rejected"), - ("ESCALATED", "Escalated"), - ], - default="PENDING", - max_length=20, - ), - ), - migrations.AlterField( - model_name="photosubmission", - name="status", - field=models.CharField( - choices=[ - ("PENDING", "Pending"), - ("APPROVED", "Approved"), - ("REJECTED", "Rejected"), - ("ESCALATED", "Escalated"), - ], - default="PENDING", - max_length=20, - ), - ), - ] diff --git a/moderation/migrations/0003_update_existing_statuses.py b/moderation/migrations/0003_update_existing_statuses.py deleted file mode 100644 index 011f417f..00000000 --- a/moderation/migrations/0003_update_existing_statuses.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.db import migrations - -def update_statuses(apps, schema_editor): - EditSubmission = apps.get_model('moderation', 'EditSubmission') - PhotoSubmission = apps.get_model('moderation', 'PhotoSubmission') - - # Update EditSubmissions - EditSubmission.objects.filter(status='NEW').update(status='PENDING') - - # Update PhotoSubmissions - PhotoSubmission.objects.filter(status='NEW').update(status='PENDING') - PhotoSubmission.objects.filter(status='AUTO_APPROVED').update(status='APPROVED') - -def reverse_statuses(apps, schema_editor): - EditSubmission = apps.get_model('moderation', 'EditSubmission') - PhotoSubmission = apps.get_model('moderation', 'PhotoSubmission') - - # Reverse EditSubmissions - EditSubmission.objects.filter(status='PENDING').update(status='NEW') - - # Reverse PhotoSubmissions - PhotoSubmission.objects.filter(status='PENDING').update(status='NEW') - -class Migration(migrations.Migration): - - dependencies = [ - ('moderation', '0002_alter_editsubmission_status_and_more'), - ] - - operations = [ - migrations.RunPython(update_statuses, reverse_statuses), - ] diff --git a/moderation/migrations/0004_add_moderator_changes.py b/moderation/migrations/0004_add_moderator_changes.py deleted file mode 100644 index d8429da4..00000000 --- a/moderation/migrations/0004_add_moderator_changes.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.1.3 on 2024-11-13 20:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("moderation", "0003_update_existing_statuses"), - ] - - operations = [ - migrations.AddField( - model_name="editsubmission", - name="moderator_changes", - field=models.JSONField( - blank=True, - help_text="Moderator's edited version of the changes before approval", - null=True, - ), - ), - ] diff --git a/moderation/migrations/0005_editsubmissionevent_photosubmissionevent_and_more.py b/moderation/migrations/0005_editsubmissionevent_photosubmissionevent_and_more.py deleted file mode 100644 index 6a59c434..00000000 --- a/moderation/migrations/0005_editsubmissionevent_photosubmissionevent_and_more.py +++ /dev/null @@ -1,305 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 15:24 - -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 = [ - ("contenttypes", "0002_remove_content_type_name"), - ("moderation", "0004_add_moderator_changes"), - ("pghistory", "0006_delete_aggregateevent"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="EditSubmissionEvent", - 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()), - ("object_id", models.PositiveIntegerField(blank=True, null=True)), - ( - "submission_type", - models.CharField( - choices=[("EDIT", "Edit Existing"), ("CREATE", "Create New")], - default="EDIT", - max_length=10, - ), - ), - ( - "changes", - models.JSONField( - help_text="JSON representation of the changes or new object data" - ), - ), - ( - "moderator_changes", - models.JSONField( - blank=True, - help_text="Moderator's edited version of the changes before approval", - null=True, - ), - ), - ( - "reason", - models.TextField(help_text="Why this edit/addition is needed"), - ), - ( - "source", - models.TextField( - blank=True, help_text="Source of information (if applicable)" - ), - ), - ( - "status", - models.CharField( - choices=[ - ("PENDING", "Pending"), - ("APPROVED", "Approved"), - ("REJECTED", "Rejected"), - ("ESCALATED", "Escalated"), - ], - default="PENDING", - max_length=20, - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("handled_at", models.DateTimeField(blank=True, null=True)), - ( - "notes", - models.TextField( - blank=True, - help_text="Notes from the moderator about this submission", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="PhotoSubmissionEvent", - 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()), - ("object_id", models.PositiveIntegerField()), - ("photo", models.ImageField(upload_to="submissions/photos/")), - ("caption", models.CharField(blank=True, max_length=255)), - ("date_taken", models.DateField(blank=True, null=True)), - ( - "status", - models.CharField( - choices=[ - ("PENDING", "Pending"), - ("APPROVED", "Approved"), - ("REJECTED", "Rejected"), - ("ESCALATED", "Escalated"), - ], - default="PENDING", - max_length=20, - ), - ), - ("created_at", models.DateTimeField(auto_now_add=True)), - ("handled_at", models.DateTimeField(blank=True, null=True)), - ( - "notes", - models.TextField( - blank=True, - help_text="Notes from the moderator about this photo submission", - ), - ), - ], - options={ - "abstract": False, - }, - ), - migrations.AlterField( - model_name="editsubmission", - name="id", - field=models.BigAutoField(primary_key=True, serialize=False), - ), - migrations.AlterField( - model_name="photosubmission", - name="id", - field=models.BigAutoField(primary_key=True, serialize=False), - ), - pgtrigger.migrations.AddTrigger( - model_name="editsubmission", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', - hash="616bbed667e6f8a1b23dfa39b5b3fd0b3bc0b43d", - operation="INSERT", - pgid="pgtrigger_insert_insert_2c796", - table="moderation_editsubmission", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="editsubmission", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "moderation_editsubmissionevent" ("changes", "content_type_id", "created_at", "handled_at", "handled_by_id", "id", "moderator_changes", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "reason", "source", "status", "submission_type", "user_id") VALUES (NEW."changes", NEW."content_type_id", NEW."created_at", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."moderator_changes", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."reason", NEW."source", NEW."status", NEW."submission_type", NEW."user_id"); RETURN NULL;', - hash="76c447d8cfeced3bb1893e2d900c97bb05a9f028", - operation="UPDATE", - pgid="pgtrigger_update_update_ab38f", - table="moderation_editsubmission", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="photosubmission", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', - hash="ea6563a26e5875de544fa270751df4f48003a4c0", - operation="INSERT", - pgid="pgtrigger_insert_insert_62865", - table="moderation_photosubmission", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="photosubmission", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "moderation_photosubmissionevent" ("caption", "content_type_id", "created_at", "date_taken", "handled_at", "handled_by_id", "id", "notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "photo", "status", "user_id") VALUES (NEW."caption", NEW."content_type_id", NEW."created_at", NEW."date_taken", NEW."handled_at", NEW."handled_by_id", NEW."id", NEW."notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."photo", NEW."status", NEW."user_id"); RETURN NULL;', - hash="64f35eedbad17d4060eaeab7f2bd944620465591", - operation="UPDATE", - pgid="pgtrigger_update_update_9c311", - table="moderation_photosubmission", - when="AFTER", - ), - ), - ), - migrations.AddField( - model_name="editsubmissionevent", - name="content_type", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="contenttypes.contenttype", - ), - ), - migrations.AddField( - model_name="editsubmissionevent", - name="handled_by", - field=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, - ), - ), - migrations.AddField( - model_name="editsubmissionevent", - 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="editsubmissionevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="moderation.editsubmission", - ), - ), - migrations.AddField( - model_name="editsubmissionevent", - name="user", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - migrations.AddField( - model_name="photosubmissionevent", - name="content_type", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="contenttypes.contenttype", - ), - ), - migrations.AddField( - model_name="photosubmissionevent", - name="handled_by", - field=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, - ), - ), - migrations.AddField( - model_name="photosubmissionevent", - 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="photosubmissionevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="moderation.photosubmission", - ), - ), - migrations.AddField( - model_name="photosubmissionevent", - name="user", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ] diff --git a/moderation/migrations/0006_merge_20250209_1447.py b/moderation/migrations/0006_merge_20250209_1447.py deleted file mode 100644 index bfb7dc78..00000000 --- a/moderation/migrations/0006_merge_20250209_1447.py +++ /dev/null @@ -1,13 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 19:47 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("moderation", "0002_add_pghistory"), - ("moderation", "0005_editsubmissionevent_photosubmissionevent_and_more"), - ] - - operations = [] diff --git a/moderation/mixins.py b/moderation/mixins.py index c17c2af5..e78bc439 100644 --- a/moderation/mixins.py +++ b/moderation/mixins.py @@ -249,10 +249,10 @@ class HistoryMixin: obj = self.get_object() # type: ignore # Get historical records ordered by date if available - history = getattr(obj, 'history', None) - if history is not None: - context['history'] = history.all().select_related('history_user').order_by('-history_date') - else: + try: + # Use pghistory's get_history method + context['history'] = obj.get_history() + except (AttributeError, TypeError): context['history'] = [] # Get related edit submissions diff --git a/parks/migrations/0001_initial.py b/parks/migrations/0001_initial.py index abee3e4f..70e6381c 100644 --- a/parks/migrations/0001_initial.py +++ b/parks/migrations/0001_initial.py @@ -1,8 +1,8 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 import django.db.models.deletion -import simple_history.models -from django.conf import settings +import pgtrigger.compiler +import pgtrigger.migrations from django.db import migrations, models @@ -12,94 +12,10 @@ class Migration(migrations.Migration): dependencies = [ ("companies", "0001_initial"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("pghistory", "0006_delete_aggregateevent"), ] operations = [ - migrations.CreateModel( - name="HistoricalPark", - fields=[ - ("id", models.BigIntegerField(blank=True, db_index=True)), - ("name", models.CharField(max_length=255)), - ("slug", models.SlugField(max_length=255)), - ("description", models.TextField(blank=True)), - ( - "status", - models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("CLOSED_TEMP", "Temporarily Closed"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("operating_season", models.CharField(blank=True, max_length=255)), - ( - "size_acres", - models.DecimalField( - blank=True, decimal_places=2, max_digits=10, null=True - ), - ), - ("website", models.URLField(blank=True)), - ( - "average_rating", - models.DecimalField( - blank=True, decimal_places=2, max_digits=3, null=True - ), - ), - ("ride_count", models.IntegerField(blank=True, null=True)), - ("coaster_count", models.IntegerField(blank=True, null=True)), - ( - "created_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "owner", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="companies.company", - ), - ), - ], - options={ - "verbose_name": "historical park", - "verbose_name_plural": "historical parks", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), migrations.CreateModel( name="Park", fields=[ @@ -157,59 +73,6 @@ class Migration(migrations.Migration): "ordering": ["name"], }, ), - migrations.CreateModel( - name="HistoricalParkArea", - fields=[ - ("id", models.BigIntegerField(blank=True, db_index=True)), - ("name", models.CharField(max_length=255)), - ("slug", models.SlugField(max_length=255)), - ("description", models.TextField(blank=True)), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ( - "created_at", - models.DateTimeField(blank=True, editable=False, null=True), - ), - ("updated_at", models.DateTimeField(blank=True, editable=False)), - ("history_id", models.AutoField(primary_key=True, serialize=False)), - ("history_date", models.DateTimeField(db_index=True)), - ("history_change_reason", models.CharField(max_length=100, null=True)), - ( - "history_type", - models.CharField( - choices=[("+", "Created"), ("~", "Changed"), ("-", "Deleted")], - max_length=1, - ), - ), - ( - "history_user", - models.ForeignKey( - null=True, - on_delete=django.db.models.deletion.SET_NULL, - related_name="+", - to=settings.AUTH_USER_MODEL, - ), - ), - ( - "park", - models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - to="parks.park", - ), - ), - ], - options={ - "verbose_name": "historical park area", - "verbose_name_plural": "historical park areas", - "ordering": ("-history_date", "-history_id"), - "get_latest_by": ("history_date", "history_id"), - }, - bases=(simple_history.models.HistoricalChanges, models.Model), - ), migrations.CreateModel( name="ParkArea", fields=[ @@ -232,7 +95,197 @@ class Migration(migrations.Migration): ], options={ "ordering": ["name"], - "unique_together": {("park", "slug")}, }, ), + migrations.CreateModel( + name="ParkAreaEvent", + 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)), + ("opening_date", models.DateField(blank=True, null=True)), + ("closing_date", models.DateField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True, null=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "park", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="parks.park", + ), + ), + ( + "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="parks.parkarea", + ), + ), + ], + options={ + "abstract": False, + }, + ), + migrations.CreateModel( + name="ParkEvent", + 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)), + ( + "status", + models.CharField( + choices=[ + ("OPERATING", "Operating"), + ("CLOSED_TEMP", "Temporarily Closed"), + ("CLOSED_PERM", "Permanently Closed"), + ("UNDER_CONSTRUCTION", "Under Construction"), + ("DEMOLISHED", "Demolished"), + ("RELOCATED", "Relocated"), + ], + default="OPERATING", + max_length=20, + ), + ), + ("opening_date", models.DateField(blank=True, null=True)), + ("closing_date", models.DateField(blank=True, null=True)), + ("operating_season", models.CharField(blank=True, max_length=255)), + ( + "size_acres", + models.DecimalField( + blank=True, decimal_places=2, max_digits=10, null=True + ), + ), + ("website", models.URLField(blank=True)), + ( + "average_rating", + models.DecimalField( + blank=True, decimal_places=2, max_digits=3, null=True + ), + ), + ("ride_count", models.IntegerField(blank=True, null=True)), + ("coaster_count", models.IntegerField(blank=True, null=True)), + ("created_at", models.DateTimeField(auto_now_add=True, null=True)), + ("updated_at", models.DateTimeField(auto_now=True)), + ( + "owner", + models.ForeignKey( + blank=True, + db_constraint=False, + null=True, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="companies.company", + ), + ), + ( + "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="parks.park", + ), + ), + ], + options={ + "abstract": False, + }, + ), + pgtrigger.migrations.AddTrigger( + model_name="park", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "opening_date", "operating_season", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."operating_season", NEW."owner_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="83eb12a74769e2601a23691085a345c29c9b6f68", + operation="INSERT", + pgid="pgtrigger_insert_insert_66883", + table="parks_park", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="park", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "opening_date", "operating_season", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."operating_season", NEW."owner_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', + hash="f42a468ec35a2d51abd5c1ae1afa41b300ae0a1b", + operation="UPDATE", + pgid="pgtrigger_update_update_19f56", + table="parks_park", + when="AFTER", + ), + ), + ), + migrations.AlterUniqueTogether( + name="parkarea", + unique_together={("park", "slug")}, + ), + pgtrigger.migrations.AddTrigger( + model_name="parkarea", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', + hash="fa64ee07f872bf2214b2c1b638b028429752bac4", + operation="INSERT", + pgid="pgtrigger_insert_insert_13457", + table="parks_parkarea", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="parkarea", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', + hash="59fa84527a4fd0fa51685058b6037fa22163a095", + operation="UPDATE", + pgid="pgtrigger_update_update_6e5aa", + table="parks_parkarea", + when="AFTER", + ), + ), + ), ] diff --git a/parks/migrations/0002_parkareaevent_parkevent_and_more.py b/parks/migrations/0002_parkareaevent_parkevent_and_more.py deleted file mode 100644 index bcebb76b..00000000 --- a/parks/migrations/0002_parkareaevent_parkevent_and_more.py +++ /dev/null @@ -1,233 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 15:44 - -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ("companies", "0004_delete_designer_alter_company_id_and_more"), - ("parks", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - ] - - operations = [ - migrations.CreateModel( - name="ParkAreaEvent", - 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)), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="ParkEvent", - 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)), - ( - "status", - models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("CLOSED_TEMP", "Temporarily Closed"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - ), - ), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("operating_season", models.CharField(blank=True, max_length=255)), - ( - "size_acres", - models.DecimalField( - blank=True, decimal_places=2, max_digits=10, null=True - ), - ), - ("website", models.URLField(blank=True)), - ( - "average_rating", - models.DecimalField( - blank=True, decimal_places=2, max_digits=3, null=True - ), - ), - ("ride_count", models.IntegerField(blank=True, null=True)), - ("coaster_count", models.IntegerField(blank=True, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=True)), - ], - options={ - "abstract": False, - }, - ), - migrations.RemoveField( - model_name="historicalpark", - name="history_user", - ), - migrations.RemoveField( - model_name="historicalpark", - name="owner", - ), - migrations.RemoveField( - model_name="historicalparkarea", - name="history_user", - ), - migrations.RemoveField( - model_name="historicalparkarea", - name="park", - ), - pgtrigger.migrations.AddTrigger( - model_name="park", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "opening_date", "operating_season", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."operating_season", NEW."owner_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="83eb12a74769e2601a23691085a345c29c9b6f68", - operation="INSERT", - pgid="pgtrigger_insert_insert_66883", - table="parks_park", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="park", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "opening_date", "operating_season", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."operating_season", NEW."owner_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', - hash="f42a468ec35a2d51abd5c1ae1afa41b300ae0a1b", - operation="UPDATE", - pgid="pgtrigger_update_update_19f56", - table="parks_park", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="parkarea", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', - hash="fa64ee07f872bf2214b2c1b638b028429752bac4", - operation="INSERT", - pgid="pgtrigger_insert_insert_13457", - table="parks_parkarea", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="parkarea", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', - hash="59fa84527a4fd0fa51685058b6037fa22163a095", - operation="UPDATE", - pgid="pgtrigger_update_update_6e5aa", - table="parks_parkarea", - when="AFTER", - ), - ), - ), - migrations.AddField( - model_name="parkareaevent", - name="park", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="parks.park", - ), - ), - migrations.AddField( - model_name="parkareaevent", - 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="parkareaevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="parks.parkarea", - ), - ), - migrations.AddField( - model_name="parkevent", - name="owner", - field=models.ForeignKey( - blank=True, - db_constraint=False, - null=True, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="+", - related_query_name="+", - to="companies.company", - ), - ), - migrations.AddField( - model_name="parkevent", - 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="parkevent", - name="pgh_obj", - field=models.ForeignKey( - db_constraint=False, - on_delete=django.db.models.deletion.DO_NOTHING, - related_name="events", - to="parks.park", - ), - ), - migrations.DeleteModel( - name="HistoricalPark", - ), - migrations.DeleteModel( - name="HistoricalParkArea", - ), - ] diff --git a/parks/migrations/0002_switch_to_pghistory.py b/parks/migrations/0002_switch_to_pghistory.py deleted file mode 100644 index e347d143..00000000 --- a/parks/migrations/0002_switch_to_pghistory.py +++ /dev/null @@ -1,154 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 17:54 - -import django.db.models.deletion -import pgtrigger.compiler -import pgtrigger.migrations -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ("parks", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - ] - - operations = [ - migrations.CreateModel( - name="ParkEvent", - 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)), - ("status", models.CharField( - choices=[ - ("OPERATING", "Operating"), - ("CLOSED_TEMP", "Temporarily Closed"), - ("CLOSED_PERM", "Permanently Closed"), - ("UNDER_CONSTRUCTION", "Under Construction"), - ("DEMOLISHED", "Demolished"), - ("RELOCATED", "Relocated"), - ], - default="OPERATING", - max_length=20, - )), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("operating_season", models.CharField(blank=True, max_length=255)), - ("size_acres", models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True)), - ("website", models.URLField(blank=True)), - ("average_rating", models.DecimalField(blank=True, decimal_places=2, max_digits=3, null=True)), - ("ride_count", models.IntegerField(blank=True, null=True)), - ("coaster_count", models.IntegerField(blank=True, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=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="parks.park")), - ("owner", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="companies.company")), - ], - options={ - "abstract": False, - }, - ), - migrations.CreateModel( - name="ParkAreaEvent", - 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)), - ("opening_date", models.DateField(blank=True, null=True)), - ("closing_date", models.DateField(blank=True, null=True)), - ("created_at", models.DateTimeField(auto_now_add=True, null=True)), - ("updated_at", models.DateTimeField(auto_now=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="parks.parkarea")), - ("park", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="parks.park")), - ], - options={ - "abstract": False, - }, - ), - pgtrigger.migrations.AddTrigger( - model_name="park", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "operating_season", "opening_date", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."operating_season", NEW."opening_date", NEW."owner_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_park", - table="parks_park", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="park", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "parks_parkevent" ("average_rating", "closing_date", "coaster_count", "created_at", "description", "id", "name", "operating_season", "opening_date", "owner_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "ride_count", "size_acres", "slug", "status", "updated_at", "website") VALUES (NEW."average_rating", NEW."closing_date", NEW."coaster_count", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."operating_season", NEW."opening_date", NEW."owner_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."ride_count", NEW."size_acres", NEW."slug", NEW."status", NEW."updated_at", NEW."website"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_park", - table="parks_park", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="parkarea", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_parkarea", - table="parks_parkarea", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="parkarea", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "parks_parkareaevent" ("closing_date", "created_at", "description", "id", "name", "opening_date", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "slug", "updated_at") VALUES (NEW."closing_date", NEW."created_at", NEW."description", NEW."id", NEW."name", NEW."opening_date", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."slug", NEW."updated_at"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_parkarea", - table="parks_parkarea", - when="AFTER", - ), - ), - ), - migrations.RemoveField( - model_name="historicalpark", - name="history_user", - ), - migrations.RemoveField( - model_name="historicalpark", - name="owner", - ), - migrations.RemoveField( - model_name="historicalparkarea", - name="history_user", - ), - migrations.RemoveField( - model_name="historicalparkarea", - name="park", - ), - migrations.DeleteModel( - name="HistoricalPark", - ), - migrations.DeleteModel( - name="HistoricalParkArea", - ), - ] \ No newline at end of file diff --git a/parks/migrations/0003_merge_20250209_1448.py b/parks/migrations/0003_merge_20250209_1448.py deleted file mode 100644 index 37489092..00000000 --- a/parks/migrations/0003_merge_20250209_1448.py +++ /dev/null @@ -1,13 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 19:48 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ("parks", "0002_parkareaevent_parkevent_and_more"), - ("parks", "0002_switch_to_pghistory"), - ] - - operations = [] diff --git a/reviews/migrations/0001_initial.py b/reviews/migrations/0001_initial.py index 05306194..20725921 100644 --- a/reviews/migrations/0001_initial.py +++ b/reviews/migrations/0001_initial.py @@ -1,7 +1,9 @@ -# Generated by Django 5.1.3 on 2024-11-12 18:07 +# Generated by Django 5.1.4 on 2025-02-10 01:10 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 @@ -12,6 +14,7 @@ class Migration(migrations.Migration): dependencies = [ ("contenttypes", "0002_remove_content_type_name"), + ("pghistory", "0006_delete_aggregateevent"), migrations.swappable_dependency(settings.AUTH_USER_MODEL), ] @@ -19,15 +22,7 @@ class Migration(migrations.Migration): migrations.CreateModel( name="Review", fields=[ - ( - "id", - models.BigAutoField( - auto_created=True, - primary_key=True, - serialize=False, - verbose_name="ID", - ), - ), + ("id", models.BigAutoField(primary_key=True, serialize=False)), ("object_id", models.PositiveIntegerField()), ( "rating", @@ -76,6 +71,87 @@ class Migration(migrations.Migration): "ordering": ["-created_at"], }, ), + migrations.CreateModel( + name="ReviewEvent", + 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()), + ("object_id", models.PositiveIntegerField()), + ( + "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)), + ( + "content_type", + models.ForeignKey( + db_constraint=False, + on_delete=django.db.models.deletion.DO_NOTHING, + related_name="+", + related_query_name="+", + to="contenttypes.contenttype", + ), + ), + ( + "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="reviews.review", + ), + ), + ( + "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, + }, + ), migrations.CreateModel( name="ReviewImage", fields=[ @@ -190,6 +266,35 @@ class Migration(migrations.Migration): name="reviews_rev_content_627d80_idx", ), ), + pgtrigger.migrations.AddTrigger( + model_name="review", + trigger=pgtrigger.compiler.Trigger( + name="insert_insert", + sql=pgtrigger.compiler.UpsertTriggerSql( + func='INSERT INTO "reviews_reviewevent" ("content", "content_type_id", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."content_type_id", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;', + hash="1126891ad95c3c8dc8580ca8b669b6c195960cff", + operation="INSERT", + pgid="pgtrigger_insert_insert_7a7c1", + table="reviews_review", + when="AFTER", + ), + ), + ), + pgtrigger.migrations.AddTrigger( + model_name="review", + trigger=pgtrigger.compiler.Trigger( + name="update_update", + sql=pgtrigger.compiler.UpsertTriggerSql( + condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", + func='INSERT INTO "reviews_reviewevent" ("content", "content_type_id", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."content_type_id", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;', + hash="091fc5e3597eddb31fed505798d29859fe8efbe0", + operation="UPDATE", + pgid="pgtrigger_update_update_b34c8", + table="reviews_review", + when="AFTER", + ), + ), + ), migrations.AlterUniqueTogether( name="reviewlike", unique_together={("review", "user")}, diff --git a/reviews/migrations/0002_add_pghistory.py b/reviews/migrations/0002_add_pghistory.py deleted file mode 100644 index aea2bd5b..00000000 --- a/reviews/migrations/0002_add_pghistory.py +++ /dev/null @@ -1,71 +0,0 @@ -# Generated by Django 5.1.4 on 2025-02-09 18:06 - -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 = [ - ("reviews", "0001_initial"), - ("pghistory", "0006_delete_aggregateevent"), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name="ReviewEvent", - 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()), - ("title", models.CharField(max_length=200)), - ("content", models.TextField()), - ("visit_date", models.DateField()), - ("created_at", models.DateTimeField()), - ("updated_at", models.DateTimeField()), - ("is_published", models.BooleanField()), - ("moderation_notes", models.TextField(blank=True)), - ("moderated_at", models.DateTimeField(blank=True, null=True)), - ("object_id", models.PositiveIntegerField()), - ("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="reviews.review")), - ("content_type", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to="contenttypes.contenttype")), - ("user", models.ForeignKey(db_constraint=False, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ("moderated_by", models.ForeignKey(blank=True, db_constraint=False, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name="+", to=settings.AUTH_USER_MODEL)), - ], - options={ - "abstract": False, - }, - ), - pgtrigger.migrations.AddTrigger( - model_name="review", - trigger=pgtrigger.compiler.Trigger( - name="insert_insert", - sql=pgtrigger.compiler.UpsertTriggerSql( - func='INSERT INTO "reviews_reviewevent" ("content", "content_type_id", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."content_type_id", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."object_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;', - operation="INSERT", - pgid="pgtrigger_insert_insert_review", - table="reviews_review", - when="AFTER", - ), - ), - ), - pgtrigger.migrations.AddTrigger( - model_name="review", - trigger=pgtrigger.compiler.Trigger( - name="update_update", - sql=pgtrigger.compiler.UpsertTriggerSql( - condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)", - func='INSERT INTO "reviews_reviewevent" ("content", "content_type_id", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "object_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."content_type_id", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."object_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;', - operation="UPDATE", - pgid="pgtrigger_update_update_review", - table="reviews_review", - when="AFTER", - ), - ), - ), - ] \ No newline at end of file diff --git a/rides/admin.py b/rides/admin.py index f62ea0a9..53f8022f 100644 --- a/rides/admin.py +++ b/rides/admin.py @@ -1,7 +1,6 @@ from django.contrib import admin from django.utils.html import format_html from django.db.models import Avg -from simple_history.admin import SimpleHistoryAdmin from .models import Ride, RollerCoasterStats class RollerCoasterStatsInline(admin.StackedInline): @@ -31,14 +30,13 @@ class RollerCoasterStatsInline(admin.StackedInline): ) @admin.register(Ride) -class RideAdmin(SimpleHistoryAdmin): +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') - history_list_display = ['status', 'manufacturer'] actions = ['mark_as_operating', 'mark_as_closed', 'mark_as_under_maintenance', 'mark_as_removed'] fieldsets = ( @@ -129,12 +127,11 @@ class RideAdmin(SimpleHistoryAdmin): mark_as_removed.short_description = "Mark selected rides as demolished" @admin.register(RollerCoasterStats) -class RollerCoasterStatsAdmin(SimpleHistoryAdmin): +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') - history_list_display = ['height_ft', 'length_ft', 'speed_mph', 'inversions'] fieldsets = ( ('Basic Stats', { diff --git a/rides/events.py b/rides/events.py new file mode 100644 index 00000000..e6e551e3 --- /dev/null +++ b/rides/events.py @@ -0,0 +1,70 @@ +from typing import Dict + +def get_ride_display_changes(changes: Dict) -> Dict: + """Returns a human-readable version of the ride changes""" + field_names = { + 'name': 'Name', + 'description': 'Description', + 'status': 'Status', + 'post_closing_status': 'Post-Closing Status', + 'opening_date': 'Opening Date', + 'closing_date': 'Closing Date', + 'status_since': 'Status Since', + 'capacity_per_hour': 'Hourly Capacity', + 'min_height_in': 'Minimum Height', + 'max_height_in': 'Maximum Height', + 'ride_duration_seconds': 'Ride Duration' + } + + display_changes = {} + for field, change in changes.items(): + if field in field_names: + old_value = change.get('old', '') + new_value = change.get('new', '') + + # Format specific fields + if field == 'status': + from .models import Ride + choices = dict(Ride.STATUS_CHOICES) + old_value = choices.get(old_value, old_value) + new_value = choices.get(new_value, new_value) + elif field == 'post_closing_status': + from .models import Ride + choices = dict(Ride.POST_CLOSING_STATUS_CHOICES) + old_value = choices.get(old_value, old_value) + new_value = choices.get(new_value, new_value) + + display_changes[field_names[field]] = { + 'old': old_value, + 'new': new_value + } + + return display_changes + +def get_ride_model_display_changes(changes: Dict) -> Dict: + """Returns a human-readable version of the ride model changes""" + field_names = { + 'name': 'Name', + 'description': 'Description', + 'category': 'Category' + } + + display_changes = {} + for field, change in changes.items(): + if field in field_names: + old_value = change.get('old', '') + new_value = change.get('new', '') + + # Format category field + if field == 'category': + from .models import CATEGORY_CHOICES + choices = dict(CATEGORY_CHOICES) + old_value = choices.get(old_value, old_value) + new_value = choices.get(new_value, new_value) + + display_changes[field_names[field]] = { + 'old': old_value, + 'new': new_value + } + + return display_changes \ No newline at end of file diff --git a/rides/migrations/0001_initial.py b/rides/migrations/0001_initial.py new file mode 100644 index 00000000..06f2625c --- /dev/null +++ b/rides/migrations/0001_initial.py @@ -0,0 +1,584 @@ +# Generated by Django 5.1.4 on 2025-02-10 01:26 + +import django.db.models.deletion +import pgtrigger.compiler +import pgtrigger.migrations +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ("companies", "0001_initial"), + ("designers", "0001_initial"), + ("parks", "0001_initial"), + ("pghistory", "0006_delete_aggregateevent"), + ] + + operations = [ + migrations.CreateModel( + name="Ride", + fields=[ + ("id", models.BigAutoField(primary_key=True, serialize=False)), + ("name", models.CharField(max_length=255)), + ("slug", models.SlugField(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, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="rides", + to="designers.designer", + ), + ), + ( + "manufacturer", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + to="companies.manufacturer", + ), + ), + ( + "park", + models.ForeignKey( + on_delete=django.db.models.deletion.CASCADE, + related_name="rides", + to="parks.park", + ), + ), + ( + "park_area", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.SET_NULL, + related_name="rides", + to="parks.parkarea", + ), + ), + ], + options={ + "ordering": ["name"], + }, + ), + 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="companies.manufacturer", + ), + ), + ], + options={ + "ordering": ["manufacturer", "name"], + "unique_together": {("manufacturer", "name")}, + }, + ), + 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="companies.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.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", + ), + ), + 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="companies.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, + }, + ), + 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", + }, + ), + 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="e9e3c3ec4cb2400b363035534c580c94a3bb1d53", + 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="4c8073b866beac402ace852e23974fcb01d24267", + operation="UPDATE", + pgid="pgtrigger_update_update_0ca1a", + table="rides_ridemodel", + when="AFTER", + ), + ), + ), + migrations.AlterUniqueTogether( + name="ride", + unique_together={("park", "slug")}, + ), + 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="870aa867ae6892f187dc0382e4a6833b5d1267c5", + 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="8bafb42256ee98b4517ae4d39d0e774111794fea", + operation="UPDATE", + pgid="pgtrigger_update_update_4917a", + table="rides_ride", + when="AFTER", + ), + ), + ), + ] diff --git a/rides/models.py b/rides/models.py index 1d1815f8..7fa852c1 100644 --- a/rides/models.py +++ b/rides/models.py @@ -1,8 +1,8 @@ from django.db import models from django.utils.text import slugify from django.contrib.contenttypes.fields import GenericRelation -from history_tracking.models import TrackedModel -import pghistory +from history_tracking.models import TrackedModel, DiffMixin +from .events import get_ride_display_changes, get_ride_model_display_changes # Shared choices that will be used by multiple models CATEGORY_CHOICES = [ @@ -15,7 +15,81 @@ CATEGORY_CHOICES = [ ('OT', 'Other'), ] -@pghistory.track() +class RideEvent(models.Model, DiffMixin): + """Event model for tracking Ride changes - uses existing pghistory table""" + + pgh_id = models.AutoField(primary_key=True) + pgh_created_at = models.DateTimeField(auto_now_add=True) + pgh_label = models.TextField() + + # Original model fields + id = models.BigIntegerField() + name = models.CharField(max_length=255) + slug = models.SlugField(max_length=255) + description = models.TextField(blank=True) + category = models.CharField(max_length=2) + status = models.CharField(max_length=20) + post_closing_status = models.CharField(max_length=20, null=True) + opening_date = models.DateField(null=True) + closing_date = models.DateField(null=True) + status_since = models.DateField(null=True) + min_height_in = models.PositiveIntegerField(null=True) + max_height_in = models.PositiveIntegerField(null=True) + capacity_per_hour = models.PositiveIntegerField(null=True) + ride_duration_seconds = models.PositiveIntegerField(null=True) + average_rating = models.DecimalField(max_digits=3, decimal_places=2, null=True) + created_at = models.DateTimeField() + updated_at = models.DateTimeField() + + # 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 + pgh_obj = models.ForeignKey('Ride', on_delete=models.CASCADE) + pgh_context = models.JSONField(null=True) + + class Meta: + db_table = 'rides_rideevent' + managed = False + + def get_display_changes(self) -> dict: + """Returns human-readable changes""" + return get_ride_display_changes(self.diff_against_previous()) + +class RideModelEvent(models.Model, DiffMixin): + """Event model for tracking RideModel changes - uses existing pghistory table""" + + pgh_id = models.AutoField(primary_key=True) + pgh_created_at = models.DateTimeField(auto_now_add=True) + pgh_label = models.TextField() + + # Original model fields + id = models.BigIntegerField() + name = models.CharField(max_length=255) + description = models.TextField(blank=True) + category = models.CharField(max_length=2) + created_at = models.DateTimeField() + updated_at = models.DateTimeField() + + # Foreign keys as IDs + manufacturer_id = models.BigIntegerField(null=True) + + # Context fields + pgh_obj = models.ForeignKey('RideModel', on_delete=models.CASCADE) + pgh_context = models.JSONField(null=True) + + class Meta: + db_table = 'rides_ridemodelevent' + managed = False + + def get_display_changes(self) -> dict: + """Returns human-readable changes""" + return get_ride_model_display_changes(self.diff_against_previous()) + class RideModel(TrackedModel): """ Represents a specific model/type of ride that can be manufactured by different companies. @@ -24,10 +98,10 @@ class RideModel(TrackedModel): name = models.CharField(max_length=255) manufacturer = models.ForeignKey( 'companies.Manufacturer', - on_delete=models.SET_NULL, # Changed to SET_NULL since it's optional + on_delete=models.SET_NULL, related_name='ride_models', - null=True, # Made optional - blank=True # Made optional + null=True, + blank=True ) description = models.TextField(blank=True) category = models.CharField( @@ -36,8 +110,6 @@ class RideModel(TrackedModel): default='', blank=True ) - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = ['manufacturer', 'name'] @@ -46,10 +118,12 @@ class RideModel(TrackedModel): def __str__(self) -> str: return self.name if not self.manufacturer else f"{self.manufacturer.name} {self.name}" -@pghistory.track() 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'), @@ -91,7 +165,7 @@ class Ride(TrackedModel): blank=True ) designer = models.ForeignKey( - 'designers.Designer', # Updated to point to the new Designer model + 'designers.Designer', on_delete=models.SET_NULL, related_name='rides', null=True, @@ -130,8 +204,6 @@ class Ride(TrackedModel): null=True, blank=True ) - created_at = models.DateTimeField(auto_now_add=True) - updated_at = models.DateTimeField(auto_now=True) photos = GenericRelation('media.Photo') reviews = GenericRelation('reviews.Review') @@ -148,6 +220,7 @@ class Ride(TrackedModel): super().save(*args, **kwargs) class RollerCoasterStats(models.Model): + """Model for tracking roller coaster specific statistics""" TRACK_MATERIAL_CHOICES = [ ('STEEL', 'Steel'), ('WOOD', 'Wood'), diff --git a/rides/views.py b/rides/views.py index 610d8788..7461da93 100644 --- a/rides/views.py +++ b/rides/views.py @@ -1,39 +1,26 @@ from typing import Any, Dict, Optional, Tuple, Union, cast, Type -from django.views.generic import DetailView, ListView, CreateView, UpdateView, RedirectView +from django.views.generic import DetailView, ListView, CreateView, UpdateView from django.shortcuts import get_object_or_404, render -from django.core.serializers.json import DjangoJSONEncoder from django.urls import reverse from django.db.models import Q, Model from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.decorators import login_required from django.contrib.contenttypes.models import ContentType from django.contrib import messages -from django.http import ( - JsonResponse, - HttpResponseRedirect, - Http404, - HttpRequest, - HttpResponse, -) +from django.http import HttpRequest, HttpResponse from django.db.models import Count -from django.core.files.uploadedfile import UploadedFile -from django.forms import ModelForm -from django.db.models.query import QuerySet -from simple_history.models import HistoricalRecords -from django.contrib.auth import get_user_model -from django.contrib.auth.models import AnonymousUser -from .models import Ride, RollerCoasterStats, RideModel, CATEGORY_CHOICES +from .models import ( + Ride, RollerCoasterStats, RideModel, RideEvent, + CATEGORY_CHOICES +) 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 media.models import Photo -from accounts.models import User from companies.models import Manufacturer from designers.models import Designer - def show_coaster_fields(request: HttpRequest) -> HttpResponse: """Show roller coaster specific fields based on category selection""" category = request.GET.get('category') @@ -41,6 +28,38 @@ def show_coaster_fields(request: HttpRequest) -> HttpResponse: return HttpResponse('') return render(request, "rides/partials/coaster_fields.html") +class RideDetailView(HistoryMixin, DetailView): + """View for displaying ride details""" + model = Ride + template_name = 'rides/ride_detail.html' + slug_url_kwarg = 'ride_slug' + + def get_queryset(self): + """Get ride for the specific park if park_slug is provided""" + queryset = Ride.objects.all().select_related( + 'park', + 'ride_model', + 'ride_model__manufacturer' + ).prefetch_related('photos') + + if 'park_slug' in self.kwargs: + queryset = queryset.filter(park__slug=self.kwargs['park_slug']) + + return queryset + + def get_context_data(self, **kwargs): + """Add context data""" + context = super().get_context_data(**kwargs) + if 'park_slug' in self.kwargs: + 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 class RideCreateView(LoginRequiredMixin, CreateView): """View for creating a new ride""" @@ -79,7 +98,6 @@ class RideCreateView(LoginRequiredMixin, CreateView): # Check for new manufacturer manufacturer_name = form.cleaned_data.get('manufacturer_search') if manufacturer_name and not form.cleaned_data.get('manufacturer'): - # Create submission for new manufacturer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Manufacturer), @@ -90,7 +108,6 @@ class RideCreateView(LoginRequiredMixin, CreateView): # Check for new designer designer_name = form.cleaned_data.get('designer_search') if designer_name and not form.cleaned_data.get('designer'): - # Create submission for new designer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Designer), @@ -102,7 +119,6 @@ class RideCreateView(LoginRequiredMixin, CreateView): ride_model_name = form.cleaned_data.get('ride_model_search') manufacturer = form.cleaned_data.get('manufacturer') if ride_model_name and not form.cleaned_data.get('ride_model') and manufacturer: - # Create submission for new ride model EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(RideModel), @@ -115,34 +131,6 @@ class RideCreateView(LoginRequiredMixin, CreateView): return super().form_valid(form) - -class RideDetailView(DetailView): - """View for displaying ride details""" - model = Ride - template_name = 'rides/ride_detail.html' - slug_url_kwarg = 'ride_slug' - - def get_queryset(self): - """Get ride for the specific park if park_slug is provided""" - queryset = Ride.objects.all().select_related( - 'park', - 'ride_model', - 'ride_model__manufacturer' - ).prefetch_related('photos') - - if 'park_slug' in self.kwargs: - queryset = queryset.filter(park__slug=self.kwargs['park_slug']) - - return queryset - - def get_context_data(self, **kwargs): - """Add park_slug to context if it exists""" - context = super().get_context_data(**kwargs) - if 'park_slug' in self.kwargs: - context['park_slug'] = self.kwargs['park_slug'] - return context - - class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): """View for updating an existing ride""" model = Ride @@ -193,7 +181,6 @@ class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): # Check for new manufacturer manufacturer_name = form.cleaned_data.get('manufacturer_search') if manufacturer_name and not form.cleaned_data.get('manufacturer'): - # Create submission for new manufacturer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Manufacturer), @@ -204,7 +191,6 @@ class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): # Check for new designer designer_name = form.cleaned_data.get('designer_search') if designer_name and not form.cleaned_data.get('designer'): - # Create submission for new designer EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(Designer), @@ -216,7 +202,6 @@ class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): ride_model_name = form.cleaned_data.get('ride_model_search') manufacturer = form.cleaned_data.get('manufacturer') if ride_model_name and not form.cleaned_data.get('ride_model') and manufacturer: - # Create submission for new ride model EditSubmission.objects.create( user=self.request.user, content_type=ContentType.objects.get_for_model(RideModel), @@ -229,7 +214,6 @@ class RideUpdateView(LoginRequiredMixin, EditSubmissionMixin, UpdateView): return super().form_valid(form) - class RideListView(ListView): """View for displaying a list of rides""" model = Ride @@ -258,7 +242,6 @@ class RideListView(ListView): context['park_slug'] = self.kwargs['park_slug'] return context - class SingleCategoryListView(ListView): """View for displaying rides of a specific category""" model = Ride @@ -291,16 +274,9 @@ class SingleCategoryListView(ListView): context['category'] = dict(CATEGORY_CHOICES).get(self.kwargs['category']) return context - # Alias for parks app to maintain backward compatibility ParkSingleCategoryListView = SingleCategoryListView - -def is_privileged_user(user: Any) -> bool: - """Check if user has privileged access""" - return bool(user and hasattr(user, 'is_staff') and (user.is_staff or user.is_superuser)) - - @login_required def search_manufacturers(request: HttpRequest) -> HttpResponse: """Search manufacturers and return results for HTMX""" @@ -318,7 +294,6 @@ def search_manufacturers(request: HttpRequest) -> HttpResponse: {"manufacturers": manufacturers, "search_term": query}, ) - @login_required def search_designers(request: HttpRequest) -> HttpResponse: """Search designers and return results for HTMX""" @@ -336,7 +311,6 @@ def search_designers(request: HttpRequest) -> HttpResponse: {"designers": designers, "search_term": query}, ) - @login_required def search_ride_models(request: HttpRequest) -> HttpResponse: """Search ride models and return results for HTMX""" diff --git a/scripts/create_initial_data.py b/scripts/create_initial_data.py new file mode 100644 index 00000000..5f2dd59b --- /dev/null +++ b/scripts/create_initial_data.py @@ -0,0 +1,101 @@ +from django.utils import timezone +from django.contrib.contenttypes.models import ContentType +from django.contrib.gis.geos import Point +from parks.models import Park +from rides.models import Ride, RideModel, RollerCoasterStats +from companies.models import Manufacturer +from location.models import Location + +# Create Cedar Point +park, _ = Park.objects.get_or_create( + name="Cedar Point", + slug="cedar-point", + defaults={ + "description": "Cedar Point is a 364-acre amusement park located on a Lake Erie peninsula in Sandusky, Ohio.", + "website": "https://www.cedarpoint.com", + "size_acres": 364, + "opening_date": timezone.datetime(1870, 1, 1).date(), # Cedar Point opened in 1870 + } +) + +# Create location for Cedar Point +Location.objects.get_or_create( + content_type=ContentType.objects.get_for_model(Park), + object_id=park.id, + defaults={ + "name": "Cedar Point", + "location_type": "amusement_park", + "street_address": "1 Cedar Point Dr", + "city": "Sandusky", + "state": "OH", + "postal_code": "44870", + "country": "USA", + "latitude": 41.4822, + "longitude": -82.6839, + "point": Point(-82.6839, 41.4822) # longitude, latitude + } +) + +# Create Intamin as manufacturer +bm, _ = Manufacturer.objects.get_or_create( + name="Intamin", + slug="intamin", + defaults={ + "description": "Intamin Amusement Rides is a design company known for creating some of the most thrilling and innovative roller coasters in the world.", + "website": "https://www.intaminworldwide.com" + } +) + +# Create Giga Coaster model +giga_model, _ = RideModel.objects.get_or_create( + name="Giga Coaster", + manufacturer=bm, + defaults={ + "description": "A roller coaster type characterized by a height between 300–399 feet and a complete circuit.", + "category": "RC" # Roller Coaster + } +) + +# Create Millennium Force +millennium, _ = Ride.objects.get_or_create( + name="Millennium Force", + slug="millennium-force", + defaults={ + "description": ( + "Millennium Force is a steel roller coaster located at Cedar Point amusement park in Sandusky, Ohio. " + "It was built by Intamin of Switzerland and opened on May 13, 2000 as the world's first giga coaster, " + "a class of roller coasters having a height between 300 and 399 feet and a complete circuit." + ), + "park": park, + "category": "RC", + "manufacturer": bm, + "ride_model": giga_model, + "status": "OPERATING", + "opening_date": timezone.datetime(2000, 5, 13).date(), + "min_height_in": 48, # 48 inches minimum height + "capacity_per_hour": 1300, + "ride_duration_seconds": 120 # 2 minutes + } +) + +# Create stats for Millennium Force +RollerCoasterStats.objects.get_or_create( + ride=millennium, + defaults={ + "height_ft": 310, + "length_ft": 6595, + "speed_mph": 93, + "inversions": 0, + "ride_time_seconds": 120, + "track_material": "STEEL", + "roller_coaster_type": "SITDOWN", + "max_drop_height_ft": 300, + "launch_type": "CHAIN", + "train_style": "Open-air stadium seating", + "trains_count": 3, + "cars_per_train": 9, + "seats_per_car": 4 + } +) + +print("Initial data created successfully!") \ No newline at end of file diff --git a/templates/rides/partials/history_panel.html b/templates/rides/partials/history_panel.html new file mode 100644 index 00000000..b982ceca --- /dev/null +++ b/templates/rides/partials/history_panel.html @@ -0,0 +1,30 @@ +
No history available.
+ {% endfor %} +No history available.
- {% endfor %} -