mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-21 08:11:08 -05:00
Add Road Trip Planner template with interactive map and trip management features
- Implemented a new HTML template for the Road Trip Planner. - Integrated Leaflet.js for interactive mapping and routing. - Added functionality for searching and selecting parks to include in a trip. - Enabled drag-and-drop reordering of selected parks. - Included trip optimization and route calculation features. - Created a summary display for trip statistics. - Added functionality to save trips and manage saved trips. - Enhanced UI with responsive design and dark mode support.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
# Generated by Django 5.1.4 on 2025-08-13 21:35
|
||||
# Generated by Django 5.2.5 on 2025-08-15 22:01
|
||||
|
||||
import django.contrib.gis.db.models.fields
|
||||
import django.contrib.postgres.fields
|
||||
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,7 +15,8 @@ class Migration(migrations.Migration):
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
("pghistory", "0006_delete_aggregateevent"),
|
||||
("pghistory", "0007_auto_20250421_0444"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
@@ -50,8 +54,8 @@ class Migration(migrations.Migration):
|
||||
("description", models.TextField(blank=True)),
|
||||
("website", models.URLField(blank=True)),
|
||||
("founded_year", models.PositiveIntegerField(blank=True, null=True)),
|
||||
("headquarters", models.CharField(blank=True, max_length=255)),
|
||||
("parks_count", models.IntegerField(default=0)),
|
||||
("rides_count", models.IntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
"verbose_name_plural": "Companies",
|
||||
@@ -153,6 +157,7 @@ class Migration(migrations.Migration):
|
||||
("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)),
|
||||
(
|
||||
"park",
|
||||
models.ForeignKey(
|
||||
@@ -179,6 +184,7 @@ class Migration(migrations.Migration):
|
||||
("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)),
|
||||
(
|
||||
"park",
|
||||
models.ForeignKey(
|
||||
@@ -308,6 +314,279 @@ class Migration(migrations.Migration):
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ParkLocation",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"point",
|
||||
django.contrib.gis.db.models.fields.PointField(
|
||||
blank=True,
|
||||
help_text="Geographic coordinates (longitude, latitude)",
|
||||
null=True,
|
||||
srid=4326,
|
||||
),
|
||||
),
|
||||
("street_address", models.CharField(blank=True, max_length=255)),
|
||||
("city", models.CharField(db_index=True, max_length=100)),
|
||||
("state", models.CharField(db_index=True, max_length=100)),
|
||||
("country", models.CharField(default="USA", max_length=100)),
|
||||
("postal_code", models.CharField(blank=True, max_length=20)),
|
||||
("highway_exit", models.CharField(blank=True, max_length=100)),
|
||||
("parking_notes", models.TextField(blank=True)),
|
||||
("best_arrival_time", models.TimeField(blank=True, null=True)),
|
||||
("seasonal_notes", models.TextField(blank=True)),
|
||||
("osm_id", models.BigIntegerField(blank=True, null=True)),
|
||||
(
|
||||
"osm_type",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
help_text="Type of OpenStreetMap object (node, way, or relation)",
|
||||
max_length=10,
|
||||
),
|
||||
),
|
||||
(
|
||||
"park",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="location",
|
||||
to="parks.park",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Park Location",
|
||||
"verbose_name_plural": "Park Locations",
|
||||
"ordering": ["park__name"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ParkReview",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"rating",
|
||||
models.PositiveSmallIntegerField(
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(1),
|
||||
django.core.validators.MaxValueValidator(10),
|
||||
]
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=200)),
|
||||
("content", models.TextField()),
|
||||
("visit_date", models.DateField()),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("is_published", models.BooleanField(default=True)),
|
||||
("moderation_notes", models.TextField(blank=True)),
|
||||
("moderated_at", models.DateTimeField(blank=True, null=True)),
|
||||
(
|
||||
"moderated_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="moderated_park_reviews",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"park",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="reviews",
|
||||
to="parks.park",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="park_reviews",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-created_at"],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ParkReviewEvent",
|
||||
fields=[
|
||||
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("pgh_label", models.TextField(help_text="The event label.")),
|
||||
("id", models.BigIntegerField()),
|
||||
(
|
||||
"rating",
|
||||
models.PositiveSmallIntegerField(
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(1),
|
||||
django.core.validators.MaxValueValidator(10),
|
||||
]
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=200)),
|
||||
("content", models.TextField()),
|
||||
("visit_date", models.DateField()),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("is_published", models.BooleanField(default=True)),
|
||||
("moderation_notes", models.TextField(blank=True)),
|
||||
("moderated_at", models.DateTimeField(blank=True, null=True)),
|
||||
(
|
||||
"moderated_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
db_constraint=False,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
related_name="+",
|
||||
related_query_name="+",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"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.parkreview",
|
||||
),
|
||||
),
|
||||
(
|
||||
"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="CompanyHeadquarters",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"street_address",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
help_text="Mailing address if publicly available",
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
(
|
||||
"city",
|
||||
models.CharField(
|
||||
db_index=True, help_text="Headquarters city", max_length=100
|
||||
),
|
||||
),
|
||||
(
|
||||
"state_province",
|
||||
models.CharField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="State/Province/Region",
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
(
|
||||
"country",
|
||||
models.CharField(
|
||||
db_index=True,
|
||||
default="USA",
|
||||
help_text="Country where headquarters is located",
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
(
|
||||
"postal_code",
|
||||
models.CharField(
|
||||
blank=True, help_text="ZIP or postal code", max_length=20
|
||||
),
|
||||
),
|
||||
(
|
||||
"mailing_address",
|
||||
models.TextField(
|
||||
blank=True,
|
||||
help_text="Complete mailing address if different from basic address",
|
||||
),
|
||||
),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
(
|
||||
"company",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="headquarters",
|
||||
to="parks.company",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Company Headquarters",
|
||||
"verbose_name_plural": "Company Headquarters",
|
||||
"ordering": ["company__name"],
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["city", "country"], name="parks_compa_city_cf9a4e_idx"
|
||||
)
|
||||
],
|
||||
},
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="park",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
@@ -342,7 +621,7 @@ class Migration(migrations.Migration):
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="insert_insert",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func='INSERT INTO "parks_parkareaevent" ("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."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;',
|
||||
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="[AWS-SECRET-REMOVED]",
|
||||
operation="INSERT",
|
||||
pgid="pgtrigger_insert_insert_13457",
|
||||
@@ -357,7 +636,7 @@ class Migration(migrations.Migration):
|
||||
name="update_update",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
|
||||
func='INSERT INTO "parks_parkareaevent" ("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."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;',
|
||||
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="[AWS-SECRET-REMOVED]",
|
||||
operation="UPDATE",
|
||||
pgid="pgtrigger_update_update_6e5aa",
|
||||
@@ -366,4 +645,43 @@ class Migration(migrations.Migration):
|
||||
),
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="parklocation",
|
||||
index=models.Index(
|
||||
fields=["city", "state"], name="parks_parkl_city_7cc873_idx"
|
||||
),
|
||||
),
|
||||
migrations.AlterUniqueTogether(
|
||||
name="parkreview",
|
||||
unique_together={("park", "user")},
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="parkreview",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="insert_insert",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func='INSERT INTO "parks_parkreviewevent" ("content", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;',
|
||||
hash="[AWS-SECRET-REMOVED]",
|
||||
operation="INSERT",
|
||||
pgid="pgtrigger_insert_insert_a99bc",
|
||||
table="parks_parkreview",
|
||||
when="AFTER",
|
||||
),
|
||||
),
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="parkreview",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="update_update",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
|
||||
func='INSERT INTO "parks_parkreviewevent" ("content", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;',
|
||||
hash="[AWS-SECRET-REMOVED]",
|
||||
operation="UPDATE",
|
||||
pgid="pgtrigger_update_update_0e40d",
|
||||
table="parks_parkreview",
|
||||
when="AFTER",
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
|
||||
17
parks/migrations/0002_alter_parkarea_unique_together.py
Normal file
17
parks/migrations/0002_alter_parkarea_unique_together.py
Normal file
@@ -0,0 +1,17 @@
|
||||
# Generated by Django 5.2.5 on 2025-08-15 22:05
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("parks", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterUniqueTogether(
|
||||
name="parkarea",
|
||||
unique_together={("park", "slug")},
|
||||
),
|
||||
]
|
||||
@@ -1,190 +0,0 @@
|
||||
# Generated by Django 5.1.4 on 2025-08-14 14:50
|
||||
|
||||
import django.core.validators
|
||||
import django.db.models.deletion
|
||||
import pgtrigger.compiler
|
||||
import pgtrigger.migrations
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("parks", "0001_initial"),
|
||||
("pghistory", "0006_delete_aggregateevent"),
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ParkReview",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"rating",
|
||||
models.PositiveSmallIntegerField(
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(1),
|
||||
django.core.validators.MaxValueValidator(10),
|
||||
]
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=200)),
|
||||
("content", models.TextField()),
|
||||
("visit_date", models.DateField()),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("is_published", models.BooleanField(default=True)),
|
||||
("moderation_notes", models.TextField(blank=True)),
|
||||
("moderated_at", models.DateTimeField(blank=True, null=True)),
|
||||
(
|
||||
"moderated_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.SET_NULL,
|
||||
related_name="moderated_park_reviews",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"park",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="reviews",
|
||||
to="parks.park",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="park_reviews",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"ordering": ["-created_at"],
|
||||
"unique_together": {("park", "user")},
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="ParkReviewEvent",
|
||||
fields=[
|
||||
("pgh_id", models.AutoField(primary_key=True, serialize=False)),
|
||||
("pgh_created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("pgh_label", models.TextField(help_text="The event label.")),
|
||||
("id", models.BigIntegerField()),
|
||||
(
|
||||
"rating",
|
||||
models.PositiveSmallIntegerField(
|
||||
validators=[
|
||||
django.core.validators.MinValueValidator(1),
|
||||
django.core.validators.MaxValueValidator(10),
|
||||
]
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=200)),
|
||||
("content", models.TextField()),
|
||||
("visit_date", models.DateField()),
|
||||
("created_at", models.DateTimeField(auto_now_add=True)),
|
||||
("updated_at", models.DateTimeField(auto_now=True)),
|
||||
("is_published", models.BooleanField(default=True)),
|
||||
("moderation_notes", models.TextField(blank=True)),
|
||||
("moderated_at", models.DateTimeField(blank=True, null=True)),
|
||||
(
|
||||
"moderated_by",
|
||||
models.ForeignKey(
|
||||
blank=True,
|
||||
db_constraint=False,
|
||||
null=True,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
related_name="+",
|
||||
related_query_name="+",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
(
|
||||
"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.parkreview",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.ForeignKey(
|
||||
db_constraint=False,
|
||||
on_delete=django.db.models.deletion.DO_NOTHING,
|
||||
related_name="+",
|
||||
related_query_name="+",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="parkreview",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="insert_insert",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
func='INSERT INTO "parks_parkreviewevent" ("content", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."park_id", _pgh_attach_context(), NOW(), \'insert\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;',
|
||||
hash="[AWS-SECRET-REMOVED]",
|
||||
operation="INSERT",
|
||||
pgid="pgtrigger_insert_insert_a99bc",
|
||||
table="parks_parkreview",
|
||||
when="AFTER",
|
||||
),
|
||||
),
|
||||
),
|
||||
pgtrigger.migrations.AddTrigger(
|
||||
model_name="parkreview",
|
||||
trigger=pgtrigger.compiler.Trigger(
|
||||
name="update_update",
|
||||
sql=pgtrigger.compiler.UpsertTriggerSql(
|
||||
condition="WHEN (OLD.* IS DISTINCT FROM NEW.*)",
|
||||
func='INSERT INTO "parks_parkreviewevent" ("content", "created_at", "id", "is_published", "moderated_at", "moderated_by_id", "moderation_notes", "park_id", "pgh_context_id", "pgh_created_at", "pgh_label", "pgh_obj_id", "rating", "title", "updated_at", "user_id", "visit_date") VALUES (NEW."content", NEW."created_at", NEW."id", NEW."is_published", NEW."moderated_at", NEW."moderated_by_id", NEW."moderation_notes", NEW."park_id", _pgh_attach_context(), NOW(), \'update\', NEW."id", NEW."rating", NEW."title", NEW."updated_at", NEW."user_id", NEW."visit_date"); RETURN NULL;',
|
||||
hash="[AWS-SECRET-REMOVED]",
|
||||
operation="UPDATE",
|
||||
pgid="pgtrigger_update_update_0e40d",
|
||||
table="parks_parkreview",
|
||||
when="AFTER",
|
||||
),
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,61 +0,0 @@
|
||||
# Generated by Django 5.1.4 on 2025-08-15 01:16
|
||||
|
||||
import django.contrib.gis.db.models.fields
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("parks", "0002_parkreview_parkreviewevent_parkreview_insert_insert_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="ParkLocation",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"point",
|
||||
django.contrib.gis.db.models.fields.PointField(
|
||||
db_index=True, srid=4326
|
||||
),
|
||||
),
|
||||
("street_address", models.CharField(blank=True, max_length=255)),
|
||||
("city", models.CharField(db_index=True, max_length=100)),
|
||||
("state", models.CharField(db_index=True, max_length=100)),
|
||||
("country", models.CharField(default="USA", max_length=100)),
|
||||
("postal_code", models.CharField(blank=True, max_length=20)),
|
||||
("highway_exit", models.CharField(blank=True, max_length=100)),
|
||||
("parking_notes", models.TextField(blank=True)),
|
||||
("best_arrival_time", models.TimeField(blank=True, null=True)),
|
||||
("osm_id", models.BigIntegerField(blank=True, null=True)),
|
||||
(
|
||||
"park",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="location",
|
||||
to="parks.park",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Park Location",
|
||||
"verbose_name_plural": "Park Locations",
|
||||
"indexes": [
|
||||
models.Index(
|
||||
fields=["city", "state"], name="parks_parkl_city_7cc873_idx"
|
||||
)
|
||||
],
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,47 +0,0 @@
|
||||
# Generated by Django 5.1.4 on 2025-08-15 01:39
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("parks", "0003_parklocation"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="company",
|
||||
name="headquarters",
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name="CompanyHeadquarters",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("city", models.CharField(db_index=True, max_length=100)),
|
||||
("state", models.CharField(db_index=True, max_length=100)),
|
||||
("country", models.CharField(default="USA", max_length=100)),
|
||||
(
|
||||
"company",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="headquarters",
|
||||
to="parks.company",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "Company Headquarters",
|
||||
"verbose_name_plural": "Company Headquarters",
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -1,46 +0,0 @@
|
||||
# Generated by Django 5.1.4 on 2025-08-15 14:11
|
||||
|
||||
import django.contrib.gis.db.models.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("parks", "0004_remove_company_headquarters_companyheadquarters"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="parklocation",
|
||||
options={
|
||||
"ordering": ["park__name"],
|
||||
"verbose_name": "Park Location",
|
||||
"verbose_name_plural": "Park Locations",
|
||||
},
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="parklocation",
|
||||
name="osm_type",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
help_text="Type of OpenStreetMap object (node, way, or relation)",
|
||||
max_length=10,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="parklocation",
|
||||
name="seasonal_notes",
|
||||
field=models.TextField(blank=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="parklocation",
|
||||
name="point",
|
||||
field=django.contrib.gis.db.models.fields.PointField(
|
||||
blank=True,
|
||||
help_text="Geographic coordinates (longitude, latitude)",
|
||||
null=True,
|
||||
srid=4326,
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,96 +0,0 @@
|
||||
# Generated by Django 5.1.4 on 2025-08-15 14:16
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("parks", "0005_alter_parklocation_options_parklocation_osm_type_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name="companyheadquarters",
|
||||
options={
|
||||
"ordering": ["company__name"],
|
||||
"verbose_name": "Company Headquarters",
|
||||
"verbose_name_plural": "Company Headquarters",
|
||||
},
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="companyheadquarters",
|
||||
name="state",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="companyheadquarters",
|
||||
name="created_at",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True, default=django.utils.timezone.now
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="companyheadquarters",
|
||||
name="mailing_address",
|
||||
field=models.TextField(
|
||||
blank=True,
|
||||
help_text="Complete mailing address if different from basic address",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="companyheadquarters",
|
||||
name="postal_code",
|
||||
field=models.CharField(
|
||||
blank=True, help_text="ZIP or postal code", max_length=20
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="companyheadquarters",
|
||||
name="state_province",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
db_index=True,
|
||||
help_text="State/Province/Region",
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="companyheadquarters",
|
||||
name="street_address",
|
||||
field=models.CharField(
|
||||
blank=True,
|
||||
help_text="Mailing address if publicly available",
|
||||
max_length=255,
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="companyheadquarters",
|
||||
name="updated_at",
|
||||
field=models.DateTimeField(auto_now=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="companyheadquarters",
|
||||
name="city",
|
||||
field=models.CharField(
|
||||
db_index=True, help_text="Headquarters city", max_length=100
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="companyheadquarters",
|
||||
name="country",
|
||||
field=models.CharField(
|
||||
db_index=True,
|
||||
default="USA",
|
||||
help_text="Country where headquarters is located",
|
||||
max_length=100,
|
||||
),
|
||||
),
|
||||
migrations.AddIndex(
|
||||
model_name="companyheadquarters",
|
||||
index=models.Index(
|
||||
fields=["city", "country"], name="parks_compa_city_cf9a4e_idx"
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -1,210 +0,0 @@
|
||||
# Generated by Django migration for location system consolidation
|
||||
|
||||
from django.db import migrations, transaction
|
||||
from django.contrib.gis.geos import Point
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
|
||||
def migrate_generic_locations_to_domain_specific(apps, schema_editor):
|
||||
"""
|
||||
Migrate data from generic Location model to domain-specific location models.
|
||||
|
||||
This migration:
|
||||
1. Migrates park locations from Location to ParkLocation
|
||||
2. Logs the migration process for verification
|
||||
3. Preserves all coordinate and address data
|
||||
"""
|
||||
# Get model references
|
||||
Location = apps.get_model('location', 'Location')
|
||||
Park = apps.get_model('parks', 'Park')
|
||||
ParkLocation = apps.get_model('parks', 'ParkLocation')
|
||||
|
||||
print("\n=== Starting Location Migration ===")
|
||||
|
||||
# Track migration statistics
|
||||
stats = {
|
||||
'parks_migrated': 0,
|
||||
'parks_skipped': 0,
|
||||
'errors': 0
|
||||
}
|
||||
|
||||
# Get content type for Park model using the migration apps registry
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
try:
|
||||
park_content_type = ContentType.objects.get(app_label='parks', model='park')
|
||||
except Exception as e:
|
||||
print(f"ERROR: Could not get ContentType for Park: {e}")
|
||||
return
|
||||
|
||||
# Find all generic locations that reference parks
|
||||
park_locations = Location.objects.filter(content_type=park_content_type)
|
||||
|
||||
print(f"Found {park_locations.count()} generic location objects for parks")
|
||||
|
||||
with transaction.atomic():
|
||||
for generic_location in park_locations:
|
||||
try:
|
||||
# Get the associated park
|
||||
try:
|
||||
park = Park.objects.get(id=generic_location.object_id)
|
||||
except Park.DoesNotExist:
|
||||
print(f"WARNING: Park with ID {generic_location.object_id} not found, skipping location")
|
||||
stats['parks_skipped'] += 1
|
||||
continue
|
||||
|
||||
# Check if ParkLocation already exists
|
||||
if hasattr(park, 'location') and park.location:
|
||||
print(f"INFO: Park '{park.name}' already has ParkLocation, skipping")
|
||||
stats['parks_skipped'] += 1
|
||||
continue
|
||||
|
||||
print(f"Migrating location for park: {park.name}")
|
||||
|
||||
# Create ParkLocation from generic Location data
|
||||
park_location_data = {
|
||||
'park': park,
|
||||
'street_address': generic_location.street_address or '',
|
||||
'city': generic_location.city or '',
|
||||
'state': generic_location.state or '',
|
||||
'country': generic_location.country or 'USA',
|
||||
'postal_code': generic_location.postal_code or '',
|
||||
}
|
||||
|
||||
# Handle coordinates - prefer point field, fall back to lat/lon
|
||||
if generic_location.point:
|
||||
park_location_data['point'] = generic_location.point
|
||||
print(f" Coordinates from point: {generic_location.point}")
|
||||
elif generic_location.latitude and generic_location.longitude:
|
||||
# Create Point from lat/lon
|
||||
park_location_data['point'] = Point(
|
||||
float(generic_location.longitude),
|
||||
float(generic_location.latitude),
|
||||
srid=4326
|
||||
)
|
||||
print(f" Coordinates from lat/lon: {generic_location.latitude}, {generic_location.longitude}")
|
||||
else:
|
||||
print(f" No coordinates available")
|
||||
|
||||
# Create the ParkLocation
|
||||
park_location = ParkLocation.objects.create(**park_location_data)
|
||||
|
||||
print(f" Created ParkLocation for {park.name}")
|
||||
stats['parks_migrated'] += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR migrating location for park {generic_location.object_id}: {e}")
|
||||
stats['errors'] += 1
|
||||
# Continue with other migrations rather than failing completely
|
||||
continue
|
||||
|
||||
# Print migration summary
|
||||
print(f"\n=== Migration Summary ===")
|
||||
print(f"Parks migrated: {stats['parks_migrated']}")
|
||||
print(f"Parks skipped: {stats['parks_skipped']}")
|
||||
print(f"Errors: {stats['errors']}")
|
||||
|
||||
# Verify migration
|
||||
print(f"\n=== Verification ===")
|
||||
total_parks = Park.objects.count()
|
||||
parks_with_location = Park.objects.filter(location__isnull=False).count()
|
||||
print(f"Total parks: {total_parks}")
|
||||
print(f"Parks with ParkLocation: {parks_with_location}")
|
||||
|
||||
if stats['errors'] == 0:
|
||||
print("✓ Migration completed successfully!")
|
||||
else:
|
||||
print(f"⚠ Migration completed with {stats['errors']} errors - check output above")
|
||||
|
||||
|
||||
def reverse_migrate_domain_specific_to_generic(apps, schema_editor):
|
||||
"""
|
||||
Reverse migration: Convert ParkLocation back to generic Location objects.
|
||||
|
||||
This is primarily for development/testing purposes.
|
||||
"""
|
||||
# Get model references
|
||||
Location = apps.get_model('location', 'Location')
|
||||
Park = apps.get_model('parks', 'Park')
|
||||
ParkLocation = apps.get_model('parks', 'ParkLocation')
|
||||
|
||||
print("\n=== Starting Reverse Migration ===")
|
||||
|
||||
stats = {
|
||||
'parks_migrated': 0,
|
||||
'errors': 0
|
||||
}
|
||||
|
||||
# Get content type for Park model using the migration apps registry
|
||||
ContentType = apps.get_model('contenttypes', 'ContentType')
|
||||
try:
|
||||
park_content_type = ContentType.objects.get(app_label='parks', model='park')
|
||||
except Exception as e:
|
||||
print(f"ERROR: Could not get ContentType for Park: {e}")
|
||||
return
|
||||
|
||||
park_locations = ParkLocation.objects.all()
|
||||
print(f"Found {park_locations.count()} ParkLocation objects to reverse migrate")
|
||||
|
||||
with transaction.atomic():
|
||||
for park_location in park_locations:
|
||||
try:
|
||||
park = park_location.park
|
||||
print(f"Reverse migrating location for park: {park.name}")
|
||||
|
||||
# Create generic Location from ParkLocation data
|
||||
location_data = {
|
||||
'content_type': park_content_type,
|
||||
'object_id': park.id,
|
||||
'name': park.name,
|
||||
'location_type': 'business',
|
||||
'street_address': park_location.street_address,
|
||||
'city': park_location.city,
|
||||
'state': park_location.state,
|
||||
'country': park_location.country,
|
||||
'postal_code': park_location.postal_code,
|
||||
}
|
||||
|
||||
# Handle coordinates
|
||||
if park_location.point:
|
||||
location_data['point'] = park_location.point
|
||||
location_data['latitude'] = park_location.point.y
|
||||
location_data['longitude'] = park_location.point.x
|
||||
|
||||
# Create the generic Location
|
||||
generic_location = Location.objects.create(**location_data)
|
||||
|
||||
print(f" Created generic Location: {generic_location}")
|
||||
stats['parks_migrated'] += 1
|
||||
|
||||
except Exception as e:
|
||||
print(f"ERROR reverse migrating location for park {park_location.park.name}: {e}")
|
||||
stats['errors'] += 1
|
||||
continue
|
||||
|
||||
print(f"\n=== Reverse Migration Summary ===")
|
||||
print(f"Parks reverse migrated: {stats['parks_migrated']}")
|
||||
print(f"Errors: {stats['errors']}")
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
"""
|
||||
Data migration to transition from generic Location model to domain-specific location models.
|
||||
|
||||
This migration moves location data from the generic location.Location model
|
||||
to the new domain-specific models like parks.ParkLocation, while preserving
|
||||
all coordinate and address information.
|
||||
"""
|
||||
|
||||
dependencies = [
|
||||
('parks', '0006_alter_companyheadquarters_options_and_more'),
|
||||
('location', '0001_initial'), # Ensure location app is available
|
||||
('contenttypes', '0002_remove_content_type_name'), # Need ContentType
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(
|
||||
migrate_generic_locations_to_domain_specific,
|
||||
reverse_migrate_domain_specific_to_generic,
|
||||
elidable=True,
|
||||
),
|
||||
]
|
||||
Reference in New Issue
Block a user