Files
thrillwiki_django_no_react/docs/park_domain_analysis.md
pacnpal 1b246eeaa4 Add comprehensive test scripts for various models and services
- Implement tests for RideLocation and CompanyHeadquarters models to verify functionality and data integrity.
- Create a manual trigger test script for trending content calculation endpoint, including authentication and unauthorized access tests.
- Develop a manufacturer sync test to ensure ride manufacturers are correctly associated with ride models.
- Add tests for ParkLocation model, including coordinate setting and distance calculations between parks.
- Implement a RoadTripService test suite covering geocoding, route calculation, park discovery, and error handling.
- Create a unified map service test script to validate map functionality, API endpoints, and performance metrics.
2025-09-27 22:26:40 -04:00

397 lines
13 KiB
Markdown

# 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.