# ThrillWiki Park Domain Analysis ## Executive Summary This document provides a complete inventory of all park-related models and their relationships across the ThrillWiki Django codebase. The analysis reveals that park-related functionality is currently distributed across three separate Django apps (`parks`, `operators`, and `property_owners`) that are always used together but artificially separated. ## Current Architecture Overview ### Apps Structure - **parks/** - Core park and park area models - **operators/** - Companies that operate theme parks - **property_owners/** - Companies that own park property - **companies/** - Empty models directory (no active models found) ## Model Inventory ### Parks App Models #### Park Model (`parks/models.py`) **Location**: `parks.models.Park` **Inheritance**: `TrackedModel` (provides history tracking) **Decorators**: `@pghistory.track()` **Fields**: - `name` - CharField(max_length=255) - `slug` - SlugField(max_length=255, unique=True) - `description` - TextField(blank=True) - `status` - CharField with choices: OPERATING, CLOSED_TEMP, CLOSED_PERM, UNDER_CONSTRUCTION, DEMOLISHED, RELOCATED - `opening_date` - DateField(null=True, blank=True) - `closing_date` - DateField(null=True, blank=True) - `operating_season` - CharField(max_length=255, blank=True) - `size_acres` - DecimalField(max_digits=10, decimal_places=2, null=True, blank=True) - `website` - URLField(blank=True) - `average_rating` - DecimalField(max_digits=3, decimal_places=2, null=True, blank=True) - `ride_count` - IntegerField(null=True, blank=True) - `coaster_count` - IntegerField(null=True, blank=True) - `created_at` - DateTimeField(auto_now_add=True, null=True) - `updated_at` - DateTimeField(auto_now=True) **Relationships**: - `operator` - ForeignKey to Operator (SET_NULL, null=True, blank=True, related_name="parks") - `property_owner` - ForeignKey to PropertyOwner (SET_NULL, null=True, blank=True, related_name="owned_parks") - `location` - GenericRelation to Location (related_query_name='park') - `photos` - GenericRelation to Photo (related_query_name="park") - `areas` - Reverse relation from ParkArea - `rides` - Reverse relation from rides app **Custom Methods**: - `get_absolute_url()` - Returns park detail URL - `get_status_color()` - Returns Tailwind CSS classes for status display - `formatted_location` (property) - Returns formatted address string - `coordinates` (property) - Returns (lat, lon) tuple - `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup - `save()` - Custom save with slug generation and historical slug tracking **Meta Options**: - `ordering = ["name"]` #### ParkArea Model (`parks/models.py`) **Location**: `parks.models.ParkArea` **Inheritance**: `TrackedModel` **Decorators**: `@pghistory.track()` **Fields**: - `name` - CharField(max_length=255) - `slug` - SlugField(max_length=255) - `description` - TextField(blank=True) - `opening_date` - DateField(null=True, blank=True) - `closing_date` - DateField(null=True, blank=True) - `created_at` - DateTimeField(auto_now_add=True, null=True) - `updated_at` - DateTimeField(auto_now=True) **Relationships**: - `park` - ForeignKey to Park (CASCADE, related_name="areas") **Custom Methods**: - `get_absolute_url()` - Returns area detail URL - `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup - `save()` - Auto-generates slug from name **Meta Options**: - `ordering = ["name"]` - `unique_together = ["park", "slug"]` ### Operators App Models #### Operator Model (`operators/models.py`) **Location**: `operators.models.Operator` **Inheritance**: `TrackedModel` **Decorators**: `@pghistory.track()` **Fields**: - `name` - CharField(max_length=255) - `slug` - SlugField(max_length=255, unique=True) - `description` - TextField(blank=True) - `website` - URLField(blank=True) - `founded_year` - PositiveIntegerField(blank=True, null=True) - `headquarters` - CharField(max_length=255, blank=True) - `parks_count` - IntegerField(default=0) - `rides_count` - IntegerField(default=0) **Custom Methods**: - `get_absolute_url()` - Returns operator detail URL - `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup - `save()` - Auto-generates slug if missing **Meta Options**: - `ordering = ['name']` - `verbose_name = 'Operator'` - `verbose_name_plural = 'Operators'` ### Property Owners App Models #### PropertyOwner Model (`property_owners/models.py`) **Location**: `property_owners.models.PropertyOwner` **Inheritance**: `TrackedModel` **Decorators**: `@pghistory.track()` **Fields**: - `name` - CharField(max_length=255) - `slug` - SlugField(max_length=255, unique=True) - `description` - TextField(blank=True) - `website` - URLField(blank=True) **Custom Methods**: - `get_absolute_url()` - Returns property owner detail URL - `get_by_slug(slug)` (classmethod) - Handles current and historical slug lookup - `save()` - Auto-generates slug if missing **Meta Options**: - `ordering = ['name']` - `verbose_name = 'Property Owner'` - `verbose_name_plural = 'Property Owners'` ### Related Models from Other Apps #### TrackedModel (`history_tracking/models.py`) **Base class for all park-related models** - Provides `created_at` and `updated_at` fields - Includes `get_history()` method for pghistory integration #### HistoricalSlug (`history_tracking/models.py`) **Tracks historical slugs for all models** - `content_type` - ForeignKey to ContentType - `object_id` - PositiveIntegerField - `slug` - SlugField(max_length=255) - `created_at` - DateTimeField - `user` - ForeignKey to User (optional) ## Relationship Diagram ```mermaid erDiagram Park ||--o{ ParkArea : contains Park }o--|| Operator : operated_by Park }o--o| PropertyOwner : owned_by Park ||--o{ Location : has_location Park ||--o{ Photo : has_photos Park ||--o{ Ride : contains Operator { int id PK string name string slug UK text description string website int founded_year string headquarters int parks_count int rides_count datetime created_at datetime updated_at } PropertyOwner { int id PK string name string slug UK text description string website datetime created_at datetime updated_at } Park { int id PK string name string slug UK text description string status date opening_date date closing_date string operating_season decimal size_acres string website decimal average_rating int ride_count int coaster_count int operator_id FK int property_owner_id FK datetime created_at datetime updated_at } ParkArea { int id PK string name string slug text description date opening_date date closing_date int park_id FK datetime created_at datetime updated_at } Location { int id PK int content_type_id FK int object_id string name string location_type decimal latitude decimal longitude string street_address string city string state string country string postal_code } Photo { int id PK int content_type_id FK int object_id string image string caption int uploaded_by_id FK } Ride { int id PK string name string slug text description string category string status int park_id FK int park_area_id FK int manufacturer_id FK int designer_id FK } ``` ## Admin Configurations ### Parks App Admin - **ParkAdmin**: List display includes location, status, operator, property_owner - **ParkAreaAdmin**: List display includes park relationship - Both use `prepopulated_fields` for slug generation - Both include `created_at` and `updated_at` as readonly fields ### Operators App Admin - **OperatorAdmin**: Shows parks_count and rides_count (readonly) - Includes founded_year filter - Search includes name, description, headquarters ### Property Owners App Admin - **PropertyOwnerAdmin**: Basic configuration - Search includes name and description - No special filters or readonly fields ## URL Patterns and Views ### Parks App URLs (`parks/urls.py`) - **List/Search**: `/` - ParkSearchView with autocomplete - **Create**: `/create/` - ParkCreateView - **Detail**: `//` - ParkDetailView with slug redirect support - **Update**: `//edit/` - ParkUpdateView - **Areas**: `//areas//` - ParkAreaDetailView - **HTMX Endpoints**: Various endpoints for dynamic content loading - **Park-specific ride categories**: Multiple URL patterns for different ride types ### Operators App URLs (`operators/urls.py`) - **List**: `/` - OperatorListView - **Detail**: `//` - OperatorDetailView with slug redirect support ### Property Owners App URLs (`property_owners/urls.py`) - **List**: `/` - PropertyOwnerListView - **Detail**: `//` - PropertyOwnerDetailView with slug redirect support ## Template Usage Patterns ### Template Structure - **parks/park_detail.html**: Comprehensive park display with operator/property owner links - **operators/operator_detail.html**: Shows operated parks with park links - **property_owners/property_owner_detail.html**: Shows owned properties with operator info ### Key Template Features - Cross-linking between parks, operators, and property owners - Conditional display of property owner (only if different from operator) - Status badges with Tailwind CSS classes - Photo galleries and location maps - History tracking display ## Shared Functionality Patterns ### Slug Generation All models use consistent slug generation: - Auto-generated from name field in `save()` method - Uses Django's `slugify()` function - Historical slug tracking via `HistoricalSlug` model ### History Tracking Implemented via two mechanisms: 1. **pghistory**: Automatic tracking with `@pghistory.track()` decorator 2. **Manual tracking**: `HistoricalSlug` model for slug changes 3. **DiffMixin**: Provides `diff_against_previous()` method for change comparison ### Slug Redirect Support All detail views use `SlugRedirectMixin` and implement `get_by_slug()` classmethod: - Checks current slug first - Falls back to pghistory events - Falls back to `HistoricalSlug` records - Returns tuple of (object, was_redirect_needed) ### Base Classes - **TrackedModel**: Provides `created_at`, `updated_at`, and history integration - **SlugRedirectMixin**: Handles historical slug redirects in views ## Key Findings ### Strengths 1. **Consistent patterns**: All models follow similar slug generation and history tracking patterns 2. **Comprehensive history**: Both automatic (pghistory) and manual (HistoricalSlug) tracking 3. **Good separation of concerns**: Clear distinction between operators and property owners 4. **Rich relationships**: Proper foreign key relationships with appropriate related_names ### Issues 1. **Artificial separation**: Three apps that are always used together 2. **Duplicated code**: Similar admin configurations and view patterns across apps 3. **Complex imports**: Cross-app imports create coupling 4. **Template redundancy**: Similar template patterns across apps ### Entity Relationship Compliance The current implementation follows the specified entity relationship rules: - ✅ Parks have required Operator relationship - ✅ Parks have optional PropertyOwner relationship - ✅ No direct Company entity references - ✅ Proper foreign key relationships with null/blank settings ## Consolidation Recommendations Based on the analysis, I recommend consolidating the three apps into a single `parks` app with the following structure: ``` parks/ ├── models/ │ ├── __init__.py # Import all models here │ ├── park.py # Park, ParkArea models │ ├── operators.py # Operator model │ └── owners.py # PropertyOwner model ├── admin/ │ ├── __init__.py # Register all admin classes │ ├── park.py # Park and ParkArea admin │ ├── operators.py # Operator admin │ └── owners.py # PropertyOwner admin ├── views/ │ ├── __init__.py # Import all views │ ├── parks.py # Park and ParkArea views │ ├── operators.py # Operator views │ └── owners.py # PropertyOwner views ├── templates/parks/ │ ├── parks/ # Park templates │ ├── operators/ # Operator templates │ └── owners/ # Property owner templates └── urls.py # All URL patterns ``` ### Benefits of Consolidation 1. **Reduced complexity**: Single app to manage instead of three 2. **Eliminated duplication**: Shared admin mixins, view base classes, and template components 3. **Simplified imports**: No cross-app dependencies 4. **Better cohesion**: Related functionality grouped together 5. **Easier maintenance**: Single location for park domain logic ### Migration Strategy 1. Create new model structure within parks app 2. Move existing models to new locations 3. Update all imports and references 4. Consolidate admin configurations 5. Merge URL patterns 6. Update template references 7. Run Django migrations to reflect changes 8. Remove empty apps This consolidation maintains all existing functionality while significantly improving code organization and maintainability.