# 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**: `/<slug>/` - ParkDetailView with slug redirect support
- **Update**: `/<slug>/edit/` - ParkUpdateView
- **Areas**: `/<park_slug>/areas/<area_slug>/` - 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**: `/<slug>/` - OperatorDetailView with slug redirect support

### Property Owners App URLs (`property_owners/urls.py`)
- **List**: `/` - PropertyOwnerListView  
- **Detail**: `/<slug>/` - 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.