feat: complete monorepo structure with frontend and shared resources

- Add complete backend/ directory with full Django application
- Add frontend/ directory with Vite + TypeScript setup ready for Next.js
- Add comprehensive shared/ directory with:
  - Complete documentation and memory-bank archives
  - Media files and avatars (letters, park/ride images)
  - Deployment scripts and automation tools
  - Shared types and utilities
- Add architecture/ directory with migration guides
- Configure pnpm workspace for monorepo development
- Update .gitignore to exclude .django_tailwind_cli/ build artifacts
- Preserve all historical documentation in shared/docs/memory-bank/
- Set up proper structure for full-stack development with shared resources
This commit is contained in:
pacnpal
2025-08-23 18:40:07 -04:00
parent b0e0678590
commit d504d41de2
762 changed files with 142636 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
from django.contrib import admin
from .models import Location
# DEPRECATED: This admin interface is deprecated.
# Location data has been migrated to domain-specific models:
# - ParkLocation in parks.models.location
# - RideLocation in rides.models.location
# - CompanyHeadquarters in parks.models.companies
#
# This admin interface is kept for data migration and cleanup purposes only.
@admin.register(Location)
class LocationAdmin(admin.ModelAdmin):
list_display = (
"name",
"location_type",
"city",
"state",
"country",
"created_at",
)
list_filter = ("location_type", "country", "state", "city")
search_fields = ("name", "street_address", "city", "state", "country")
readonly_fields = ("created_at", "updated_at", "content_type", "object_id")
fieldsets = (
(
"⚠️ DEPRECATED MODEL",
{
"description": "This model is deprecated. Use domain-specific location models instead.",
"fields": (),
},
),
("Basic Information", {"fields": ("name", "location_type")}),
("Geographic Coordinates", {"fields": ("latitude", "longitude")}),
(
"Address",
{
"fields": (
"street_address",
"city",
"state",
"country",
"postal_code",
)
},
),
(
"Content Type (Read Only)",
{
"fields": ("content_type", "object_id"),
"classes": ("collapse",),
},
),
(
"Metadata",
{"fields": ("created_at", "updated_at"), "classes": ("collapse",)},
),
)
def get_queryset(self, request):
return super().get_queryset(request).select_related("content_type")
def has_add_permission(self, request):
# Prevent creating new generic Location objects
return False

View File

@@ -0,0 +1,8 @@
from django.apps import AppConfig
import os
class LocationConfig(AppConfig):
path = os.path.dirname(os.path.abspath(__file__))
default_auto_field = "django.db.models.BigAutoField"
name = "apps.location"

View File

@@ -0,0 +1,42 @@
# DEPRECATED: These forms are deprecated and no longer used.
#
# Domain-specific location models now have their own forms:
# - ParkLocationForm in parks.forms (for ParkLocation)
# - RideLocationForm in rides.forms (for RideLocation)
# - CompanyHeadquartersForm in parks.forms (for CompanyHeadquarters)
#
# This file is kept for reference during migration cleanup only.
from django import forms
from .models import Location
# NOTE: All classes below are DEPRECATED
# Use domain-specific location forms instead
class LocationForm(forms.ModelForm):
"""DEPRECATED: Use domain-specific location forms instead"""
class Meta:
model = Location
fields = [
"name",
"location_type",
"latitude",
"longitude",
"street_address",
"city",
"state",
"country",
"postal_code",
]
class LocationSearchForm(forms.Form):
"""DEPRECATED: Location search functionality has been moved to parks app"""
query = forms.CharField(
max_length=255,
required=True,
help_text="This form is deprecated. Use location search in the parks app.",
)

View File

@@ -0,0 +1,293 @@
# Generated by Django 5.1.4 on 2025-08-13 21:35
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):
initial = True
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("pghistory", "0006_delete_aggregateevent"),
]
operations = [
migrations.CreateModel(
name="Location",
fields=[
(
"id",
models.BigAutoField(
auto_created=True,
primary_key=True,
serialize=False,
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(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"content_type",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
to="contenttypes.contenttype",
),
),
],
options={
"ordering": ["name"],
},
),
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="[AWS-SECRET-REMOVED]",
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="[AWS-SECRET-REMOVED]",
operation="UPDATE",
pgid="pgtrigger_update_update_471d2",
table="location_location",
when="AFTER",
),
),
),
]

View File

@@ -0,0 +1,53 @@
# Generated by Django 5.2.5 on 2025-08-16 17:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("contenttypes", "0002_remove_content_type_name"),
("location", "0001_initial"),
]
operations = [
migrations.AddConstraint(
model_name="location",
constraint=models.CheckConstraint(
condition=models.Q(
("latitude__isnull", True),
models.Q(("latitude__gte", -90), ("latitude__lte", 90)),
_connector="OR",
),
name="location_latitude_range",
violation_error_message="Latitude must be between -90 and 90 degrees",
),
),
migrations.AddConstraint(
model_name="location",
constraint=models.CheckConstraint(
condition=models.Q(
("longitude__isnull", True),
models.Q(("longitude__gte", -180), ("longitude__lte", 180)),
_connector="OR",
),
name="location_longitude_range",
violation_error_message="Longitude must be between -180 and 180 degrees",
),
),
migrations.AddConstraint(
model_name="location",
constraint=models.CheckConstraint(
condition=models.Q(
models.Q(("latitude__isnull", True), ("longitude__isnull", True)),
models.Q(
("latitude__isnull", False),
("longitude__isnull", False),
),
_connector="OR",
),
name="location_coordinates_complete",
violation_error_message="Both latitude and longitude must be provided together",
),
),
]

View File

@@ -0,0 +1,175 @@
from django.contrib.gis.db import models as gis_models
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.core.validators import MinValueValidator, MaxValueValidator
from django.contrib.gis.geos import Point
import pghistory
from apps.core.history import TrackedModel
@pghistory.track()
class Location(TrackedModel):
"""
A generic location model that can be associated with any model
using GenericForeignKey. Stores detailed location information
including coordinates and address components.
"""
# Generic relation fields
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey("content_type", "object_id")
# Location name and type
name = models.CharField(
max_length=255,
help_text="Name of the location (e.g. business name, landmark)",
)
location_type = models.CharField(
max_length=50,
help_text="Type of location (e.g. business, landmark, address)",
)
# Geographic coordinates
latitude = models.DecimalField(
max_digits=9,
decimal_places=6,
validators=[MinValueValidator(-90), MaxValueValidator(90)],
help_text="Latitude coordinate (legacy field)",
null=True,
blank=True,
)
longitude = models.DecimalField(
max_digits=9,
decimal_places=6,
validators=[MinValueValidator(-180), MaxValueValidator(180)],
help_text="Longitude coordinate (legacy field)",
null=True,
blank=True,
)
# GeoDjango point field
point = gis_models.PointField(
srid=4326, # WGS84 coordinate system
null=True,
blank=True,
help_text="Geographic coordinates as a Point",
)
# Address components
street_address = models.CharField(max_length=255, blank=True, null=True)
city = models.CharField(max_length=100, blank=True, null=True)
state = models.CharField(
max_length=100,
blank=True,
null=True,
help_text="State/Region/Province",
)
country = models.CharField(max_length=100, blank=True, null=True)
postal_code = models.CharField(max_length=20, blank=True, null=True)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
indexes = [
models.Index(fields=["content_type", "object_id"]),
models.Index(fields=["city"]),
models.Index(fields=["country"]),
]
ordering = ["name"]
constraints = [
# Business rule: Latitude must be within valid range (-90 to 90)
models.CheckConstraint(
name="location_latitude_range",
check=models.Q(latitude__isnull=True)
| (models.Q(latitude__gte=-90) & models.Q(latitude__lte=90)),
violation_error_message="Latitude must be between -90 and 90 degrees",
),
# Business rule: Longitude must be within valid range (-180 to 180)
models.CheckConstraint(
name="location_longitude_range",
check=models.Q(longitude__isnull=True)
| (models.Q(longitude__gte=-180) & models.Q(longitude__lte=180)),
violation_error_message="Longitude must be between -180 and 180 degrees",
),
# Business rule: If coordinates are provided, both lat and lng must
# be present
models.CheckConstraint(
name="location_coordinates_complete",
check=models.Q(latitude__isnull=True, longitude__isnull=True)
| models.Q(latitude__isnull=False, longitude__isnull=False),
violation_error_message="Both latitude and longitude must be provided together",
),
]
def __str__(self):
location_parts = []
if self.city:
location_parts.append(self.city)
if self.country:
location_parts.append(self.country)
location_str = (
", ".join(location_parts) if location_parts else "Unknown location"
)
return f"{self.name} ({location_str})"
def save(self, *args, **kwargs):
# Sync point field with lat/lon fields for backward compatibility
if self.latitude is not None and self.longitude is not None and not self.point:
self.point = Point(float(self.longitude), float(self.latitude))
elif self.point and (self.latitude is None or self.longitude is None):
self.longitude = self.point.x
self.latitude = self.point.y
super().save(*args, **kwargs)
def get_formatted_address(self):
"""Returns a formatted address string"""
components = []
if self.street_address:
components.append(self.street_address)
if self.city:
components.append(self.city)
if self.state:
components.append(self.state)
if self.postal_code:
components.append(self.postal_code)
if self.country:
components.append(self.country)
return ", ".join(components) if components else ""
@property
def coordinates(self):
"""Returns coordinates as a tuple"""
if self.point:
# Returns (latitude, longitude)
return (self.point.y, self.point.x)
elif self.latitude is not None and self.longitude is not None:
return (float(self.latitude), float(self.longitude))
return None
def distance_to(self, other_location):
"""
Calculate the distance to another location in meters.
Returns None if either location is missing coordinates.
"""
if not self.point or not other_location.point:
return None
return self.point.distance(other_location.point) * 100000 # Convert to meters
def nearby_locations(self, distance_km=10):
"""
Find locations within specified distance in kilometers.
Returns a queryset of nearby Location objects.
"""
if not self.point:
return Location.objects.none()
return Location.objects.filter(
point__distance_lte=(
self.point,
distance_km * 1000,
) # Convert km to meters
).exclude(pk=self.pk)

View File

@@ -0,0 +1,181 @@
from django.test import TestCase
from django.contrib.contenttypes.models import ContentType
from django.contrib.gis.geos import Point
from .models import Location
from apps.parks.models import Park, Company as Operator
class LocationModelTests(TestCase):
def setUp(self):
# Create test company
self.operator = Operator.objects.create(
name="Test Operator", website="http://example.com"
)
# Create test park
self.park = Park.objects.create(
name="Test Park", owner=self.operator, status="OPERATING"
)
# Create test location for company
self.operator_location = Location.objects.create(
content_type=ContentType.objects.get_for_model(Operator),
object_id=self.operator.pk,
name="Test Operator HQ",
location_type="business",
street_address="123 Operator St",
city="Operator City",
state="CS",
country="Test Country",
postal_code="12345",
point=Point(-118.2437, 34.0522), # Los Angeles coordinates
)
# Create test location for park
self.park_location = Location.objects.create(
content_type=ContentType.objects.get_for_model(Park),
object_id=self.park.pk,
name="Test Park Location",
location_type="park",
street_address="456 Park Ave",
city="Park City",
state="PC",
country="Test Country",
postal_code="67890",
point=Point(-111.8910, 40.7608), # Park City coordinates
)
def test_location_creation(self):
"""Test location instance creation and field values"""
# Test company location
self.assertEqual(self.operator_location.name, "Test Operator HQ")
self.assertEqual(self.operator_location.location_type, "business")
self.assertEqual(self.operator_location.street_address, "123 Operator St")
self.assertEqual(self.operator_location.city, "Operator City")
self.assertEqual(self.operator_location.state, "CS")
self.assertEqual(self.operator_location.country, "Test Country")
self.assertEqual(self.operator_location.postal_code, "12345")
self.assertIsNotNone(self.operator_location.point)
# Test park location
self.assertEqual(self.park_location.name, "Test Park Location")
self.assertEqual(self.park_location.location_type, "park")
self.assertEqual(self.park_location.street_address, "456 Park Ave")
self.assertEqual(self.park_location.city, "Park City")
self.assertEqual(self.park_location.state, "PC")
self.assertEqual(self.park_location.country, "Test Country")
self.assertEqual(self.park_location.postal_code, "67890")
self.assertIsNotNone(self.park_location.point)
def test_location_str_representation(self):
"""Test string representation of location"""
expected_company_str = "Test Operator HQ (Operator City, Test Country)"
self.assertEqual(str(self.operator_location), expected_company_str)
expected_park_str = "Test Park Location (Park City, Test Country)"
self.assertEqual(str(self.park_location), expected_park_str)
def test_get_formatted_address(self):
"""Test get_formatted_address method"""
expected_address = "123 Operator St, Operator City, CS, 12345, Test Country"
self.assertEqual(
self.operator_location.get_formatted_address(), expected_address
)
def test_point_coordinates(self):
"""Test point coordinates"""
# Test company location point
self.assertIsNotNone(self.operator_location.point)
self.assertAlmostEqual(
self.operator_location.point.y, 34.0522, places=4
) # latitude
self.assertAlmostEqual(
self.operator_location.point.x, -118.2437, places=4
) # longitude
# Test park location point
self.assertIsNotNone(self.park_location.point)
self.assertAlmostEqual(
self.park_location.point.y, 40.7608, places=4
) # latitude
self.assertAlmostEqual(
self.park_location.point.x, -111.8910, places=4
) # longitude
def test_coordinates_property(self):
"""Test coordinates property"""
company_coords = self.operator_location.coordinates
self.assertIsNotNone(company_coords)
self.assertAlmostEqual(company_coords[0], 34.0522, places=4) # latitude
self.assertAlmostEqual(company_coords[1], -118.2437, places=4) # longitude
park_coords = self.park_location.coordinates
self.assertIsNotNone(park_coords)
self.assertAlmostEqual(park_coords[0], 40.7608, places=4) # latitude
self.assertAlmostEqual(park_coords[1], -111.8910, places=4) # longitude
def test_distance_calculation(self):
"""Test distance_to method"""
distance = self.operator_location.distance_to(self.park_location)
self.assertIsNotNone(distance)
self.assertGreater(distance, 0)
def test_nearby_locations(self):
"""Test nearby_locations method"""
# Create another location near the company location
nearby_location = Location.objects.create(
content_type=ContentType.objects.get_for_model(Operator),
object_id=self.operator.pk,
name="Nearby Location",
location_type="business",
street_address="789 Nearby St",
city="Operator City",
country="Test Country",
point=Point(-118.2438, 34.0523), # Very close to company location
)
nearby = self.operator_location.nearby_locations(distance_km=1)
self.assertEqual(nearby.count(), 1)
self.assertEqual(nearby.first(), nearby_location)
def test_content_type_relations(self):
"""Test generic relations work correctly"""
# Test company location relation
company_location = Location.objects.get(
content_type=ContentType.objects.get_for_model(Operator),
object_id=self.operator.pk,
)
self.assertEqual(company_location, self.operator_location)
# Test park location relation
park_location = Location.objects.get(
content_type=ContentType.objects.get_for_model(Park),
object_id=self.park.pk,
)
self.assertEqual(park_location, self.park_location)
def test_location_updates(self):
"""Test location updates"""
# Update company location
self.operator_location.street_address = "Updated Address"
self.operator_location.city = "Updated City"
self.operator_location.save()
updated_location = Location.objects.get(pk=self.operator_location.pk)
self.assertEqual(updated_location.street_address, "Updated Address")
self.assertEqual(updated_location.city, "Updated City")
def test_point_sync_with_lat_lon(self):
"""Test point synchronization with latitude/longitude fields"""
location = Location.objects.create(
content_type=ContentType.objects.get_for_model(Operator),
object_id=self.operator.pk,
name="Test Sync Location",
location_type="business",
latitude=34.0522,
longitude=-118.2437,
)
self.assertIsNotNone(location.point)
self.assertAlmostEqual(location.point.y, 34.0522, places=4)
self.assertAlmostEqual(location.point.x, -118.2437, places=4)

View File

@@ -0,0 +1,31 @@
# DEPRECATED: These URLs are deprecated and no longer used.
#
# Location search functionality has been moved to the parks app:
# - /parks/search/location/ (replaces /location/search/)
# - /parks/search/reverse-geocode/ (replaces /location/reverse-geocode/)
#
# Domain-specific location models are managed through their respective apps:
# - Parks app for ParkLocation
# - Rides app for RideLocation
# - Parks app for CompanyHeadquarters
#
# This file is kept for reference during migration cleanup only.
from django.urls import path
from . import views
app_name = "location"
# NOTE: All URLs below are DEPRECATED
# The location app URLs should not be included in the main URLconf
urlpatterns = [
# DEPRECATED: Use /parks/search/location/ instead
path("search/", views.LocationSearchView.as_view(), name="search"),
# DEPRECATED: Use /parks/search/reverse-geocode/ instead
path("reverse-geocode/", views.reverse_geocode, name="reverse_geocode"),
# DEPRECATED: Use domain-specific location models instead
path("create/", views.LocationCreateView.as_view(), name="create"),
path("<int:pk>/update/", views.LocationUpdateView.as_view(), name="update"),
path("<int:pk>/delete/", views.LocationDeleteView.as_view(), name="delete"),
]

View File

@@ -0,0 +1,48 @@
# DEPRECATED: These views are deprecated and no longer used.
#
# Location search functionality has been moved to the parks app:
# - parks.views.location_search
# - parks.views.reverse_geocode
#
# Domain-specific location models are now used instead of the generic Location model:
# - ParkLocation in parks.models.location
# - RideLocation in rides.models.location
# - CompanyHeadquarters in parks.models.companies
#
# This file is kept for reference during migration cleanup only.
from django.views.generic import View
from django.http import JsonResponse
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.decorators.http import require_http_methods
# NOTE: All classes and functions below are DEPRECATED
# Use the equivalent functionality in the parks app instead
class LocationSearchView(View):
"""DEPRECATED: Use parks.views.location_search instead"""
class LocationCreateView(LoginRequiredMixin, View):
"""DEPRECATED: Use domain-specific location models instead"""
class LocationUpdateView(LoginRequiredMixin, View):
"""DEPRECATED: Use domain-specific location models instead"""
class LocationDeleteView(LoginRequiredMixin, View):
"""DEPRECATED: Use domain-specific location models instead"""
@require_http_methods(["GET"])
def reverse_geocode(request):
"""DEPRECATED: Use parks.views.reverse_geocode instead"""
return JsonResponse(
{
"error": "This endpoint is deprecated. Use /parks/search/reverse-geocode/ instead"
},
status=410,
)