mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 18:31:09 -05:00
Add comprehensive system architecture and feature documentation for ThrillWiki
This commit is contained in:
410
memory-bank/documentation/APIs.md
Normal file
410
memory-bank/documentation/APIs.md
Normal file
@@ -0,0 +1,410 @@
|
|||||||
|
# API Documentation
|
||||||
|
|
||||||
|
## API Overview
|
||||||
|
|
||||||
|
### Base Configuration
|
||||||
|
```python
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
|
||||||
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
|
],
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
|
'rest_framework.permissions.IsAuthenticated',
|
||||||
|
],
|
||||||
|
'DEFAULT_PAGINATION_CLASS':
|
||||||
|
'rest_framework.pagination.PageNumberPagination',
|
||||||
|
'PAGE_SIZE': 20,
|
||||||
|
'DEFAULT_VERSIONING_CLASS':
|
||||||
|
'rest_framework.versioning.AcceptHeaderVersioning',
|
||||||
|
'DEFAULT_VERSION': 'v1'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### JWT Authentication
|
||||||
|
```http
|
||||||
|
POST /api/token/
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"username": "user@example.com",
|
||||||
|
"[PASSWORD-REMOVED]"
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"access": "eyJ0eXAiOiJKV1QiLCJhbGc...",
|
||||||
|
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Refresh
|
||||||
|
```http
|
||||||
|
POST /api/token/refresh/
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGc..."
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"access": "eyJ0eXAiOiJKV1QiLCJhbGc..."
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
|
||||||
|
### Parks API
|
||||||
|
|
||||||
|
#### List Parks
|
||||||
|
```http
|
||||||
|
GET /api/v1/parks/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"count": 100,
|
||||||
|
"next": "http://api.thrillwiki.com/parks/?page=2",
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Adventure Park",
|
||||||
|
"slug": "adventure-park",
|
||||||
|
"status": "OPERATING",
|
||||||
|
"description": "...",
|
||||||
|
"location": {
|
||||||
|
"city": "Orlando",
|
||||||
|
"state": "FL",
|
||||||
|
"country": "USA"
|
||||||
|
},
|
||||||
|
"ride_count": 25,
|
||||||
|
"average_rating": 4.5
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Park Detail
|
||||||
|
```http
|
||||||
|
GET /api/v1/parks/{slug}/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Adventure Park",
|
||||||
|
"slug": "adventure-park",
|
||||||
|
"status": "OPERATING",
|
||||||
|
"description": "...",
|
||||||
|
"location": {
|
||||||
|
"address": "123 Theme Park Way",
|
||||||
|
"city": "Orlando",
|
||||||
|
"state": "FL",
|
||||||
|
"country": "USA",
|
||||||
|
"postal_code": "32819",
|
||||||
|
"coordinates": {
|
||||||
|
"latitude": 28.538336,
|
||||||
|
"longitude": -81.379234
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"owner": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Theme Park Corp",
|
||||||
|
"verified": true
|
||||||
|
},
|
||||||
|
"stats": {
|
||||||
|
"ride_count": 25,
|
||||||
|
"coaster_count": 5,
|
||||||
|
"average_rating": 4.5
|
||||||
|
},
|
||||||
|
"rides": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Thrill Coaster",
|
||||||
|
"type": "ROLLER_COASTER",
|
||||||
|
"status": "OPERATING"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rides API
|
||||||
|
|
||||||
|
#### List Rides
|
||||||
|
```http
|
||||||
|
GET /api/v1/parks/{park_slug}/rides/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"count": 25,
|
||||||
|
"next": null,
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Thrill Coaster",
|
||||||
|
"slug": "thrill-coaster",
|
||||||
|
"type": "ROLLER_COASTER",
|
||||||
|
"status": "OPERATING",
|
||||||
|
"height_requirement": 48,
|
||||||
|
"thrill_rating": 5,
|
||||||
|
"manufacturer": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Coaster Corp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Get Ride Detail
|
||||||
|
```http
|
||||||
|
GET /api/v1/rides/{ride_slug}/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Thrill Coaster",
|
||||||
|
"slug": "thrill-coaster",
|
||||||
|
"type": "ROLLER_COASTER",
|
||||||
|
"status": "OPERATING",
|
||||||
|
"description": "...",
|
||||||
|
"specifications": {
|
||||||
|
"height_requirement": 48,
|
||||||
|
"thrill_rating": 5,
|
||||||
|
"capacity_per_hour": 1200,
|
||||||
|
"track_length": 3000
|
||||||
|
},
|
||||||
|
"manufacturer": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "Coaster Corp"
|
||||||
|
},
|
||||||
|
"designer": {
|
||||||
|
"id": 1,
|
||||||
|
"name": "John Designer"
|
||||||
|
},
|
||||||
|
"opening_date": "2020-06-15",
|
||||||
|
"stats": {
|
||||||
|
"average_rating": 4.8,
|
||||||
|
"review_count": 150
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Reviews API
|
||||||
|
|
||||||
|
#### Create Review
|
||||||
|
```http
|
||||||
|
POST /api/v1/reviews/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"content_type": "ride",
|
||||||
|
"object_id": 1,
|
||||||
|
"rating": 5,
|
||||||
|
"content": "Amazing experience!",
|
||||||
|
"media": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"file": "base64encoded..."
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"author": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "reviewer"
|
||||||
|
},
|
||||||
|
"rating": 5,
|
||||||
|
"content": "Amazing experience!",
|
||||||
|
"status": "PENDING",
|
||||||
|
"created_at": "2024-02-18T14:30:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### List Reviews
|
||||||
|
```http
|
||||||
|
GET /api/v1/rides/{ride_id}/reviews/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"count": 150,
|
||||||
|
"next": "http://api.thrillwiki.com/rides/1/reviews/?page=2",
|
||||||
|
"previous": null,
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"author": {
|
||||||
|
"id": 1,
|
||||||
|
"username": "reviewer"
|
||||||
|
},
|
||||||
|
"rating": 5,
|
||||||
|
"content": "Amazing experience!",
|
||||||
|
"created_at": "2024-02-18T14:30:00Z",
|
||||||
|
"media": [
|
||||||
|
{
|
||||||
|
"type": "image",
|
||||||
|
"url": "https://media.thrillwiki.com/reviews/1/image.jpg"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integrations
|
||||||
|
|
||||||
|
### Email Service Integration
|
||||||
|
```http
|
||||||
|
POST /api/v1/email/send/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"template": "review_notification",
|
||||||
|
"recipient": "user@example.com",
|
||||||
|
"context": {
|
||||||
|
"review_id": 1,
|
||||||
|
"content": "Amazing experience!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"status": "sent",
|
||||||
|
"message_id": "123abc",
|
||||||
|
"sent_at": "2024-02-18T14:30:00Z"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Media Processing
|
||||||
|
```http
|
||||||
|
POST /api/v1/media/process/
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
Content-Type: multipart/form-data
|
||||||
|
|
||||||
|
file: [binary data]
|
||||||
|
|
||||||
|
Response:
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"original_url": "https://media.thrillwiki.com/original/image.jpg",
|
||||||
|
"processed_url": "https://media.thrillwiki.com/processed/image.jpg",
|
||||||
|
"thumbnail_url": "https://media.thrillwiki.com/thumbnails/image.jpg",
|
||||||
|
"metadata": {
|
||||||
|
"width": 1920,
|
||||||
|
"height": 1080,
|
||||||
|
"format": "jpeg",
|
||||||
|
"size": 1024576
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Versioning
|
||||||
|
|
||||||
|
### Version Header
|
||||||
|
```http
|
||||||
|
Accept: application/json; version=1.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Version Routes
|
||||||
|
```python
|
||||||
|
# urls.py
|
||||||
|
urlpatterns = [
|
||||||
|
path('v1/', include('api.v1.urls')),
|
||||||
|
path('v2/', include('api.v2.urls')),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Error Response Format
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": {
|
||||||
|
"code": "validation_error",
|
||||||
|
"message": "Invalid input data",
|
||||||
|
"details": [
|
||||||
|
{
|
||||||
|
"field": "rating",
|
||||||
|
"message": "Rating must be between 1 and 5"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Common Error Codes
|
||||||
|
- `authentication_error`: Invalid or missing authentication
|
||||||
|
- `permission_denied`: Insufficient permissions
|
||||||
|
- `validation_error`: Invalid input data
|
||||||
|
- `not_found`: Resource not found
|
||||||
|
- `rate_limit_exceeded`: Too many requests
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
### Rate Limit Configuration
|
||||||
|
```python
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_THROTTLE_CLASSES': [
|
||||||
|
'rest_framework.throttling.AnonRateThrottle',
|
||||||
|
'rest_framework.throttling.UserRateThrottle'
|
||||||
|
],
|
||||||
|
'DEFAULT_THROTTLE_RATES': {
|
||||||
|
'anon': '100/day',
|
||||||
|
'user': '1000/day',
|
||||||
|
'burst': '20/minute'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rate Limit Headers
|
||||||
|
```http
|
||||||
|
X-RateLimit-Limit: 1000
|
||||||
|
X-RateLimit-Remaining: 999
|
||||||
|
X-RateLimit-Reset: 1613664000
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Documentation
|
||||||
|
|
||||||
|
### Swagger/OpenAPI
|
||||||
|
```yaml
|
||||||
|
openapi: 3.0.0
|
||||||
|
info:
|
||||||
|
title: ThrillWiki API
|
||||||
|
version: 1.0.0
|
||||||
|
paths:
|
||||||
|
/parks:
|
||||||
|
get:
|
||||||
|
summary: List parks
|
||||||
|
parameters:
|
||||||
|
- name: page
|
||||||
|
in: query
|
||||||
|
schema:
|
||||||
|
type: integer
|
||||||
|
responses:
|
||||||
|
'200':
|
||||||
|
description: Successful response
|
||||||
|
content:
|
||||||
|
application/json:
|
||||||
|
schema:
|
||||||
|
$ref: '#/components/schemas/ParkList'
|
||||||
|
```
|
||||||
|
|
||||||
|
### API Documentation URLs
|
||||||
|
```python
|
||||||
|
urlpatterns = [
|
||||||
|
path('docs/', include_docs_urls(title='ThrillWiki API')),
|
||||||
|
path('schema/', schema_view),
|
||||||
|
]
|
||||||
168
memory-bank/documentation/Architecture.md
Normal file
168
memory-bank/documentation/Architecture.md
Normal file
@@ -0,0 +1,168 @@
|
|||||||
|
# System Architecture Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
ThrillWiki is a Django-based web platform built with a modular architecture focusing on theme park information management, user reviews, and content moderation.
|
||||||
|
|
||||||
|
## Technology Stack
|
||||||
|
|
||||||
|
### Backend
|
||||||
|
- **Framework**: Django 5.1.6
|
||||||
|
- **API**: Django REST Framework 3.15.2
|
||||||
|
- **WebSocket Support**: Channels 4.2.0 with Redis
|
||||||
|
- **Authentication**: django-allauth, OAuth Toolkit
|
||||||
|
- **Database**: PostgreSQL with django-pghistory
|
||||||
|
|
||||||
|
### Frontend
|
||||||
|
- **Templating**: Django Templates
|
||||||
|
- **CSS Framework**: Tailwind CSS
|
||||||
|
- **Enhancement**: HTMX, JavaScript
|
||||||
|
- **Asset Management**: django-webpack-loader
|
||||||
|
|
||||||
|
### Infrastructure
|
||||||
|
- **Static Files**: WhiteNoise 6.9.0
|
||||||
|
- **Media Storage**: Local filesystem with custom storage backends
|
||||||
|
- **Caching**: Redis (shared with WebSocket layer)
|
||||||
|
|
||||||
|
## System Components
|
||||||
|
|
||||||
|
### Core Applications
|
||||||
|
|
||||||
|
1. **Parks Module**
|
||||||
|
- Park information management
|
||||||
|
- Geographic data handling
|
||||||
|
- Operating hours tracking
|
||||||
|
- Integration with location services
|
||||||
|
|
||||||
|
2. **Rides Module**
|
||||||
|
- Ride specifications
|
||||||
|
- Manufacturer/Designer attribution
|
||||||
|
- Historical data tracking
|
||||||
|
- Technical details management
|
||||||
|
|
||||||
|
3. **Reviews System**
|
||||||
|
- User-generated content
|
||||||
|
- Media attachments
|
||||||
|
- Rating framework
|
||||||
|
- Integration with moderation
|
||||||
|
|
||||||
|
4. **Moderation System**
|
||||||
|
- Content review workflow
|
||||||
|
- Quality control mechanisms
|
||||||
|
- User management
|
||||||
|
- Verification processes
|
||||||
|
|
||||||
|
5. **Companies Module**
|
||||||
|
- Company profiles
|
||||||
|
- Verification system
|
||||||
|
- Official update management
|
||||||
|
- Park operator features
|
||||||
|
|
||||||
|
### Service Layer
|
||||||
|
|
||||||
|
1. **Authentication Service**
|
||||||
|
```python
|
||||||
|
# Key authentication flows
|
||||||
|
User Authentication → JWT Token → Protected Resources
|
||||||
|
Social Auth → Profile Creation → Platform Access
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **Media Service**
|
||||||
|
```python
|
||||||
|
# Media handling workflow
|
||||||
|
Upload → Processing → Storage → Delivery
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Analytics Service**
|
||||||
|
```python
|
||||||
|
# Analytics pipeline
|
||||||
|
User Action → Event Tracking → Processing → Insights
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Flow Architecture
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
||||||
|
│ Client │ ──→ │ Django │ ──→ │ Database │
|
||||||
|
│ Browser │ ←── │ Server │ ←── │ (Postgres) │
|
||||||
|
└─────────────┘ └──────────────┘ └─────────────┘
|
||||||
|
↑ ↓
|
||||||
|
┌──────────────┐
|
||||||
|
│ Services │
|
||||||
|
│ (Redis/S3) │
|
||||||
|
└──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
1. **Authentication Flow**
|
||||||
|
- JWT-based authentication
|
||||||
|
- Social authentication integration
|
||||||
|
- Session management
|
||||||
|
- Permission-based access control
|
||||||
|
|
||||||
|
2. **Data Protection**
|
||||||
|
- Input validation
|
||||||
|
- XSS prevention
|
||||||
|
- CSRF protection
|
||||||
|
- SQL injection prevention
|
||||||
|
|
||||||
|
## Deployment Model
|
||||||
|
|
||||||
|
### Production Environment
|
||||||
|
```
|
||||||
|
├── Application Server (Daphne/ASGI)
|
||||||
|
├── Database (PostgreSQL)
|
||||||
|
├── Cache/Message Broker (Redis)
|
||||||
|
├── Static Files (WhiteNoise)
|
||||||
|
└── Media Storage (Filesystem/S3)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Development Environment
|
||||||
|
```
|
||||||
|
├── Local Django Server
|
||||||
|
├── Local PostgreSQL
|
||||||
|
├── Local Redis
|
||||||
|
└── Local File Storage
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring and Scaling
|
||||||
|
|
||||||
|
1. **Performance Monitoring**
|
||||||
|
- Page load metrics
|
||||||
|
- Database query analysis
|
||||||
|
- Cache hit rates
|
||||||
|
- API response times
|
||||||
|
|
||||||
|
2. **Scaling Strategy**
|
||||||
|
- Horizontal scaling of web servers
|
||||||
|
- Database read replicas
|
||||||
|
- Cache layer expansion
|
||||||
|
- Media CDN integration
|
||||||
|
|
||||||
|
## Integration Points
|
||||||
|
|
||||||
|
1. **External Services**
|
||||||
|
- Email service (ForwardEmail.net)
|
||||||
|
- Social authentication providers
|
||||||
|
- Geographic data services
|
||||||
|
- Media processing services
|
||||||
|
|
||||||
|
2. **Internal Services**
|
||||||
|
- WebSocket notifications
|
||||||
|
- Background tasks
|
||||||
|
- Media processing
|
||||||
|
- Analytics processing
|
||||||
|
|
||||||
|
## System Requirements
|
||||||
|
|
||||||
|
### Minimum Requirements
|
||||||
|
- Python 3.11+
|
||||||
|
- PostgreSQL 13+
|
||||||
|
- Redis 6+
|
||||||
|
- Node.js 18+ (for frontend builds)
|
||||||
|
|
||||||
|
### Development Tools
|
||||||
|
- black (code formatting)
|
||||||
|
- flake8 (linting)
|
||||||
|
- pytest (testing)
|
||||||
|
- tailwind CLI (CSS processing)
|
||||||
287
memory-bank/documentation/Code.md
Normal file
287
memory-bank/documentation/Code.md
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
# Code Documentation
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
thrillwiki/
|
||||||
|
├── accounts/ # User management
|
||||||
|
├── analytics/ # Usage tracking
|
||||||
|
├── companies/ # Company profiles
|
||||||
|
├── core/ # Core functionality
|
||||||
|
├── designers/ # Designer profiles
|
||||||
|
├── email_service/ # Email handling
|
||||||
|
├── history/ # Historical views
|
||||||
|
├── history_tracking/ # Change tracking
|
||||||
|
├── location/ # Geographic features
|
||||||
|
├── media/ # Media management
|
||||||
|
├── moderation/ # Content moderation
|
||||||
|
├── parks/ # Park management
|
||||||
|
├── reviews/ # Review system
|
||||||
|
└── rides/ # Ride management
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Patterns
|
||||||
|
|
||||||
|
### 1. Model Patterns
|
||||||
|
|
||||||
|
#### History Tracking
|
||||||
|
```python
|
||||||
|
@pghistory.track()
|
||||||
|
class TrackedModel(models.Model):
|
||||||
|
"""Base class for models with history tracking"""
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Slug Management
|
||||||
|
```python
|
||||||
|
class SluggedModel:
|
||||||
|
"""Pattern for models with slug-based URLs"""
|
||||||
|
@classmethod
|
||||||
|
def get_by_slug(cls, slug: str) -> Tuple[Model, bool]:
|
||||||
|
# Check current slugs
|
||||||
|
try:
|
||||||
|
return cls.objects.get(slug=slug), False
|
||||||
|
except cls.DoesNotExist:
|
||||||
|
# Check historical slugs
|
||||||
|
historical = HistoricalSlug.objects.filter(
|
||||||
|
content_type=ContentType.objects.get_for_model(cls),
|
||||||
|
slug=slug
|
||||||
|
).first()
|
||||||
|
if historical:
|
||||||
|
return cls.objects.get(pk=historical.object_id), True
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Generic Relations
|
||||||
|
```python
|
||||||
|
# Example from parks/models.py
|
||||||
|
class Park(TrackedModel):
|
||||||
|
location = GenericRelation(Location)
|
||||||
|
photos = GenericRelation(Photo)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. View Patterns
|
||||||
|
|
||||||
|
#### Class-Based Views
|
||||||
|
```python
|
||||||
|
class ModeratedCreateView(LoginRequiredMixin, CreateView):
|
||||||
|
"""Base view for content requiring moderation"""
|
||||||
|
def form_valid(self, form):
|
||||||
|
obj = form.save(commit=False)
|
||||||
|
obj.status = 'PENDING'
|
||||||
|
obj.created_by = self.request.user
|
||||||
|
return super().form_valid(form)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Permission Mixins
|
||||||
|
```python
|
||||||
|
class ModeratorRequiredMixin:
|
||||||
|
"""Ensures user has moderation permissions"""
|
||||||
|
def dispatch(self, request, *args, **kwargs):
|
||||||
|
if not request.user.has_perm('moderation.can_moderate'):
|
||||||
|
raise PermissionDenied
|
||||||
|
return super().dispatch(request, *args, **kwargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Service Patterns
|
||||||
|
|
||||||
|
#### Email Service
|
||||||
|
```python
|
||||||
|
class EmailService:
|
||||||
|
"""Handles email templating and sending"""
|
||||||
|
def send_moderation_notification(self, content):
|
||||||
|
template = 'moderation/email/notification.html'
|
||||||
|
context = {'content': content}
|
||||||
|
self.send_templated_email(template, context)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Media Processing
|
||||||
|
```python
|
||||||
|
class MediaProcessor:
|
||||||
|
"""Handles image optimization and processing"""
|
||||||
|
def process_image(self, image):
|
||||||
|
# Optimize size
|
||||||
|
# Extract EXIF
|
||||||
|
# Generate thumbnails
|
||||||
|
return processed_image
|
||||||
|
```
|
||||||
|
|
||||||
|
## Dependencies
|
||||||
|
|
||||||
|
### Core Dependencies
|
||||||
|
```toml
|
||||||
|
# From pyproject.toml
|
||||||
|
[tool.poetry.dependencies]
|
||||||
|
django = "5.1.6"
|
||||||
|
djangorestframework = "3.15.2"
|
||||||
|
django-allauth = "65.4.1"
|
||||||
|
psycopg2-binary = "2.9.10"
|
||||||
|
django-pghistory = "3.5.2"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Dependencies
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"tailwindcss": "^3.0.0",
|
||||||
|
"htmx": "^1.22.0",
|
||||||
|
"webpack": "^5.0.0"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build Configuration
|
||||||
|
|
||||||
|
### Django Settings
|
||||||
|
```python
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
# Django apps
|
||||||
|
'django.contrib.admin',
|
||||||
|
'django.contrib.auth',
|
||||||
|
|
||||||
|
# Third-party apps
|
||||||
|
'allauth',
|
||||||
|
'rest_framework',
|
||||||
|
'corsheaders',
|
||||||
|
|
||||||
|
# Local apps
|
||||||
|
'parks.apps.ParksConfig',
|
||||||
|
'rides.apps.RidesConfig',
|
||||||
|
]
|
||||||
|
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
'whitenoise.middleware.WhiteNoiseMiddleware',
|
||||||
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Configuration
|
||||||
|
```python
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': env('DB_NAME'),
|
||||||
|
'USER': env('DB_USER'),
|
||||||
|
'PASSWORD': env('DB_PASSWORD'),
|
||||||
|
'HOST': env('DB_HOST'),
|
||||||
|
'PORT': env('DB_PORT'),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing Framework
|
||||||
|
|
||||||
|
### Test Structure
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── unit/ # Unit tests
|
||||||
|
├── integration/ # Integration tests
|
||||||
|
└── e2e/ # End-to-end tests
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Patterns
|
||||||
|
```python
|
||||||
|
class ParkTestCase(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.park = Park.objects.create(
|
||||||
|
name="Test Park",
|
||||||
|
status="OPERATING"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_park_creation(self):
|
||||||
|
self.assertEqual(self.park.slug, "test-park")
|
||||||
|
```
|
||||||
|
|
||||||
|
## Package Management
|
||||||
|
|
||||||
|
### Python Dependencies
|
||||||
|
```bash
|
||||||
|
# Development dependencies
|
||||||
|
pip install -r requirements-dev.txt
|
||||||
|
|
||||||
|
# Production dependencies
|
||||||
|
pip install -r requirements.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
### Frontend Build
|
||||||
|
```bash
|
||||||
|
# Install frontend dependencies
|
||||||
|
npm install
|
||||||
|
|
||||||
|
# Build static assets
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Quality Tools
|
||||||
|
|
||||||
|
### Python Tools
|
||||||
|
- black (code formatting)
|
||||||
|
- flake8 (linting)
|
||||||
|
- mypy (type checking)
|
||||||
|
- pytest (testing)
|
||||||
|
|
||||||
|
### Configuration Files
|
||||||
|
```toml
|
||||||
|
# pyproject.toml
|
||||||
|
[tool.black]
|
||||||
|
line-length = 88
|
||||||
|
target-version = ['py311']
|
||||||
|
|
||||||
|
[tool.mypy]
|
||||||
|
plugins = ["mypy_django_plugin.main"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Local Development
|
||||||
|
1. Set up virtual environment
|
||||||
|
2. Install dependencies
|
||||||
|
3. Run migrations
|
||||||
|
4. Start development server
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python -m venv venv
|
||||||
|
source venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python manage.py migrate
|
||||||
|
python manage.py runserver
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Review Process
|
||||||
|
1. Run linting tools
|
||||||
|
2. Run test suite
|
||||||
|
3. Check type hints
|
||||||
|
4. Review documentation
|
||||||
|
|
||||||
|
## Deployment Process
|
||||||
|
|
||||||
|
### Pre-deployment Checks
|
||||||
|
1. Run test suite
|
||||||
|
2. Check migrations
|
||||||
|
3. Validate static files
|
||||||
|
4. Verify environment variables
|
||||||
|
|
||||||
|
### Deployment Steps
|
||||||
|
1. Update dependencies
|
||||||
|
2. Apply migrations
|
||||||
|
3. Collect static files
|
||||||
|
4. Restart application server
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
### Exception Pattern
|
||||||
|
```python
|
||||||
|
class CustomException(Exception):
|
||||||
|
"""Base exception for application"""
|
||||||
|
def __init__(self, message, code=None):
|
||||||
|
self.message = message
|
||||||
|
self.code = code
|
||||||
|
```
|
||||||
|
|
||||||
|
### Middleware Pattern
|
||||||
|
```python
|
||||||
|
class ErrorHandlingMiddleware:
|
||||||
|
"""Centralized error handling"""
|
||||||
|
def process_exception(self, request, exception):
|
||||||
|
# Log exception
|
||||||
|
# Handle gracefully
|
||||||
|
# Return appropriate response
|
||||||
327
memory-bank/documentation/Data.md
Normal file
327
memory-bank/documentation/Data.md
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
# Data Documentation
|
||||||
|
|
||||||
|
## Database Schema
|
||||||
|
|
||||||
|
### Core Models
|
||||||
|
|
||||||
|
#### Parks
|
||||||
|
```sql
|
||||||
|
CREATE TABLE parks_park (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
slug VARCHAR(255) UNIQUE NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
status VARCHAR(20) DEFAULT 'OPERATING',
|
||||||
|
opening_date DATE,
|
||||||
|
closing_date DATE,
|
||||||
|
operating_season VARCHAR(255),
|
||||||
|
size_acres DECIMAL(10,2),
|
||||||
|
website VARCHAR(200),
|
||||||
|
average_rating DECIMAL(3,2),
|
||||||
|
ride_count INTEGER,
|
||||||
|
coaster_count INTEGER,
|
||||||
|
owner_id INTEGER REFERENCES companies_company(id),
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rides
|
||||||
|
```sql
|
||||||
|
CREATE TABLE rides_ride (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL,
|
||||||
|
slug VARCHAR(255) NOT NULL,
|
||||||
|
description TEXT,
|
||||||
|
status VARCHAR(20),
|
||||||
|
park_id INTEGER REFERENCES parks_park(id),
|
||||||
|
area_id INTEGER REFERENCES parks_parkarea(id),
|
||||||
|
manufacturer_id INTEGER REFERENCES companies_company(id),
|
||||||
|
designer_id INTEGER REFERENCES designers_designer(id),
|
||||||
|
opening_date DATE,
|
||||||
|
closing_date DATE,
|
||||||
|
height_requirement INTEGER,
|
||||||
|
ride_type VARCHAR(50),
|
||||||
|
thrill_rating INTEGER,
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP,
|
||||||
|
UNIQUE(park_id, slug)
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Reviews
|
||||||
|
```sql
|
||||||
|
CREATE TABLE reviews_review (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
content TEXT NOT NULL,
|
||||||
|
rating DECIMAL(3,2),
|
||||||
|
status VARCHAR(20),
|
||||||
|
author_id INTEGER REFERENCES auth_user(id),
|
||||||
|
content_type_id INTEGER REFERENCES django_content_type(id),
|
||||||
|
object_id INTEGER,
|
||||||
|
created_at TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Entity Relationships
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
erDiagram
|
||||||
|
Park ||--o{ ParkArea : "contains"
|
||||||
|
Park ||--o{ Ride : "has"
|
||||||
|
Park ||--o{ Photo : "has"
|
||||||
|
Park ||--o{ Review : "receives"
|
||||||
|
ParkArea ||--o{ Ride : "contains"
|
||||||
|
Ride ||--o{ Photo : "has"
|
||||||
|
Ride ||--o{ Review : "receives"
|
||||||
|
Company ||--o{ Park : "owns"
|
||||||
|
Company ||--o{ Ride : "manufactures"
|
||||||
|
Designer ||--o{ Ride : "designs"
|
||||||
|
User ||--o{ Review : "writes"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Models
|
||||||
|
|
||||||
|
### Content Models
|
||||||
|
|
||||||
|
#### Park Model
|
||||||
|
- Core information about theme parks
|
||||||
|
- Location data through GenericRelation
|
||||||
|
- Media attachments
|
||||||
|
- Historical tracking
|
||||||
|
- Owner relationship
|
||||||
|
|
||||||
|
#### Ride Model
|
||||||
|
- Technical specifications
|
||||||
|
- Park and area relationships
|
||||||
|
- Manufacturer and designer links
|
||||||
|
- Operation status tracking
|
||||||
|
- Safety requirements
|
||||||
|
|
||||||
|
#### Review Model
|
||||||
|
- Generic foreign key for flexibility
|
||||||
|
- Rating system
|
||||||
|
- Media attachments
|
||||||
|
- Moderation status
|
||||||
|
- Author tracking
|
||||||
|
|
||||||
|
### Supporting Models
|
||||||
|
|
||||||
|
#### Location Model
|
||||||
|
```python
|
||||||
|
class Location(models.Model):
|
||||||
|
content_type = models.ForeignKey(ContentType)
|
||||||
|
object_id = models.PositiveIntegerField()
|
||||||
|
content_object = GenericForeignKey()
|
||||||
|
|
||||||
|
address = models.CharField(max_length=255)
|
||||||
|
city = models.CharField(max_length=100)
|
||||||
|
state = models.CharField(max_length=100)
|
||||||
|
country = models.CharField(max_length=100)
|
||||||
|
postal_code = models.CharField(max_length=20)
|
||||||
|
latitude = models.DecimalField(max_digits=9, decimal_places=6)
|
||||||
|
longitude = models.DecimalField(max_digits=9, decimal_places=6)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Media Model
|
||||||
|
```python
|
||||||
|
class Photo(models.Model):
|
||||||
|
content_type = models.ForeignKey(ContentType)
|
||||||
|
object_id = models.PositiveIntegerField()
|
||||||
|
content_object = GenericForeignKey()
|
||||||
|
|
||||||
|
file = models.ImageField(upload_to='photos/')
|
||||||
|
caption = models.CharField(max_length=255)
|
||||||
|
taken_at = models.DateTimeField(null=True)
|
||||||
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Storage Strategies
|
||||||
|
|
||||||
|
### Database Storage
|
||||||
|
|
||||||
|
#### PostgreSQL Configuration
|
||||||
|
```python
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'thrillwiki',
|
||||||
|
'CONN_MAX_AGE': 60,
|
||||||
|
'OPTIONS': {
|
||||||
|
'client_encoding': 'UTF8',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Indexing Strategy
|
||||||
|
```sql
|
||||||
|
-- Performance indexes
|
||||||
|
CREATE INDEX idx_park_slug ON parks_park(slug);
|
||||||
|
CREATE INDEX idx_ride_slug ON rides_ride(slug);
|
||||||
|
CREATE INDEX idx_review_content_type ON reviews_review(content_type_id, object_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
### File Storage
|
||||||
|
|
||||||
|
#### Media Storage
|
||||||
|
```python
|
||||||
|
# Media storage configuration
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||||
|
MEDIA_URL = '/media/'
|
||||||
|
|
||||||
|
# File upload handlers
|
||||||
|
FILE_UPLOAD_HANDLERS = [
|
||||||
|
'django.core.files.uploadhandler.MemoryFileUploadHandler',
|
||||||
|
'django.core.files.uploadhandler.TemporaryFileUploadHandler',
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Directory Structure
|
||||||
|
```
|
||||||
|
media/
|
||||||
|
├── photos/
|
||||||
|
│ ├── parks/
|
||||||
|
│ ├── rides/
|
||||||
|
│ └── reviews/
|
||||||
|
├── avatars/
|
||||||
|
└── documents/
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caching Strategy
|
||||||
|
|
||||||
|
#### Cache Configuration
|
||||||
|
```python
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
||||||
|
'LOCATION': 'redis://127.0.0.1:6379/1',
|
||||||
|
'OPTIONS': {
|
||||||
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache Keys
|
||||||
|
```python
|
||||||
|
# Cache key patterns
|
||||||
|
CACHE_KEYS = {
|
||||||
|
'park_detail': 'park:{slug}',
|
||||||
|
'ride_list': 'park:{park_slug}:rides',
|
||||||
|
'review_count': 'content:{type}:{id}:reviews',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Migration
|
||||||
|
|
||||||
|
### Migration Strategy
|
||||||
|
1. Schema migrations via Django
|
||||||
|
2. Data migrations for model changes
|
||||||
|
3. Content migrations for large updates
|
||||||
|
|
||||||
|
### Example Migration
|
||||||
|
```python
|
||||||
|
# migrations/0002_add_park_status.py
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('parks', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='park',
|
||||||
|
name='status',
|
||||||
|
field=models.CharField(
|
||||||
|
max_length=20,
|
||||||
|
choices=[
|
||||||
|
('OPERATING', 'Operating'),
|
||||||
|
('CLOSED', 'Closed'),
|
||||||
|
],
|
||||||
|
default='OPERATING'
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Protection
|
||||||
|
|
||||||
|
### Backup Strategy
|
||||||
|
1. Daily database backups
|
||||||
|
2. Media files backup
|
||||||
|
3. Retention policy management
|
||||||
|
|
||||||
|
### Backup Configuration
|
||||||
|
```python
|
||||||
|
# backup settings
|
||||||
|
BACKUP_ROOT = os.path.join(BASE_DIR, 'backups')
|
||||||
|
BACKUP_RETENTION_DAYS = 30
|
||||||
|
BACKUP_COMPRESSION = True
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Validation
|
||||||
|
|
||||||
|
### Model Validation
|
||||||
|
```python
|
||||||
|
class Park(models.Model):
|
||||||
|
def clean(self):
|
||||||
|
if self.closing_date and self.opening_date:
|
||||||
|
if self.closing_date < self.opening_date:
|
||||||
|
raise ValidationError({
|
||||||
|
'closing_date': 'Closing date cannot be before opening date'
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Form Validation
|
||||||
|
```python
|
||||||
|
class RideForm(forms.ModelForm):
|
||||||
|
def clean_height_requirement(self):
|
||||||
|
height = self.cleaned_data['height_requirement']
|
||||||
|
if height and height < 0:
|
||||||
|
raise forms.ValidationError('Height requirement cannot be negative')
|
||||||
|
return height
|
||||||
|
```
|
||||||
|
|
||||||
|
## Data Access Patterns
|
||||||
|
|
||||||
|
### QuerySet Optimization
|
||||||
|
```python
|
||||||
|
# Optimized query pattern
|
||||||
|
Park.objects.select_related('owner')\
|
||||||
|
.prefetch_related('rides', 'areas')\
|
||||||
|
.filter(status='OPERATING')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caching Pattern
|
||||||
|
```python
|
||||||
|
def get_park_detail(slug):
|
||||||
|
cache_key = f'park:{slug}'
|
||||||
|
park = cache.get(cache_key)
|
||||||
|
if not park:
|
||||||
|
park = Park.objects.get(slug=slug)
|
||||||
|
cache.set(cache_key, park, timeout=3600)
|
||||||
|
return park
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring and Metrics
|
||||||
|
|
||||||
|
### Database Metrics
|
||||||
|
- Query performance
|
||||||
|
- Cache hit rates
|
||||||
|
- Storage usage
|
||||||
|
- Connection pool status
|
||||||
|
|
||||||
|
### Collection Configuration
|
||||||
|
```python
|
||||||
|
LOGGING = {
|
||||||
|
'handlers': {
|
||||||
|
'db_log': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'class': 'logging.handlers.RotatingFileHandler',
|
||||||
|
'filename': 'logs/db.log',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
253
memory-bank/documentation/Features.md
Normal file
253
memory-bank/documentation/Features.md
Normal file
@@ -0,0 +1,253 @@
|
|||||||
|
# Feature Documentation
|
||||||
|
|
||||||
|
## Core Features
|
||||||
|
|
||||||
|
### 1. Park Management
|
||||||
|
|
||||||
|
#### Park Discovery
|
||||||
|
- Geographic search and filtering
|
||||||
|
- Park categorization and taxonomy
|
||||||
|
- Operating hours and seasonal information
|
||||||
|
- Location-based recommendations
|
||||||
|
|
||||||
|
#### Park Profiles
|
||||||
|
- Detailed park information
|
||||||
|
- Historical data and timeline
|
||||||
|
- Media galleries
|
||||||
|
- Operating schedule management
|
||||||
|
- Accessibility information
|
||||||
|
|
||||||
|
#### Area Management
|
||||||
|
```python
|
||||||
|
# Key relationships
|
||||||
|
Park
|
||||||
|
└── Areas
|
||||||
|
└── Rides
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Ride System
|
||||||
|
|
||||||
|
#### Ride Catalog
|
||||||
|
- Technical specifications
|
||||||
|
- Thrill ratings and categories
|
||||||
|
- Operational status tracking
|
||||||
|
- Maintenance history
|
||||||
|
- Designer and manufacturer attribution
|
||||||
|
|
||||||
|
#### Ride Features
|
||||||
|
- Height requirements
|
||||||
|
- Accessibility options
|
||||||
|
- Queue management information
|
||||||
|
- Rider experience details
|
||||||
|
- Historical modifications
|
||||||
|
|
||||||
|
### 3. Review System
|
||||||
|
|
||||||
|
#### User Reviews
|
||||||
|
- Rating framework
|
||||||
|
- Experience descriptions
|
||||||
|
- Visit date tracking
|
||||||
|
- Media attachments
|
||||||
|
- Helpful vote system
|
||||||
|
|
||||||
|
#### Review Workflow
|
||||||
|
```
|
||||||
|
Submission → Moderation → Publication → Feedback
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Review Features
|
||||||
|
- Rich text formatting
|
||||||
|
- Multi-media support
|
||||||
|
- Rating categories
|
||||||
|
- Experience verification
|
||||||
|
- Response management
|
||||||
|
|
||||||
|
### 4. User Management
|
||||||
|
|
||||||
|
#### User Profiles
|
||||||
|
- Activity history
|
||||||
|
- Contribution tracking
|
||||||
|
- Reputation system
|
||||||
|
- Privacy controls
|
||||||
|
|
||||||
|
#### Authentication
|
||||||
|
- Email registration
|
||||||
|
- Social authentication
|
||||||
|
- Password management
|
||||||
|
- Session control
|
||||||
|
|
||||||
|
#### Permissions
|
||||||
|
- Role-based access
|
||||||
|
- Content moderation rights
|
||||||
|
- Company verification
|
||||||
|
- Expert designation
|
||||||
|
|
||||||
|
### 5. Company Management
|
||||||
|
|
||||||
|
#### Company Profiles
|
||||||
|
- Official park operator accounts
|
||||||
|
- Manufacturer profiles
|
||||||
|
- Designer portfolios
|
||||||
|
- Verification system
|
||||||
|
|
||||||
|
#### Official Updates
|
||||||
|
- Park announcements
|
||||||
|
- Operational updates
|
||||||
|
- New attraction information
|
||||||
|
- Special event coverage
|
||||||
|
|
||||||
|
### 6. Media Management
|
||||||
|
|
||||||
|
#### Image Handling
|
||||||
|
- Multi-format support
|
||||||
|
- EXIF data processing
|
||||||
|
- Automatic optimization
|
||||||
|
- Gallery organization
|
||||||
|
|
||||||
|
#### Storage System
|
||||||
|
```python
|
||||||
|
# Media organization
|
||||||
|
content/
|
||||||
|
├── parks/
|
||||||
|
├── rides/
|
||||||
|
├── reviews/
|
||||||
|
└── profiles/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7. Location Services
|
||||||
|
|
||||||
|
#### Geographic Features
|
||||||
|
- Park proximity search
|
||||||
|
- Regional categorization
|
||||||
|
- Map integration
|
||||||
|
- Distance calculations
|
||||||
|
|
||||||
|
#### Location Data
|
||||||
|
- Coordinate system
|
||||||
|
- Address validation
|
||||||
|
- Region management
|
||||||
|
- Geographic clustering
|
||||||
|
|
||||||
|
### 8. Analytics System
|
||||||
|
|
||||||
|
#### Tracking Features
|
||||||
|
- Page view analytics
|
||||||
|
- User engagement metrics
|
||||||
|
- Content popularity
|
||||||
|
- Search patterns
|
||||||
|
|
||||||
|
#### Trend Analysis
|
||||||
|
- Popular content
|
||||||
|
- User behavior
|
||||||
|
- Seasonal patterns
|
||||||
|
- Content quality metrics
|
||||||
|
|
||||||
|
## Business Requirements
|
||||||
|
|
||||||
|
### 1. Content Quality
|
||||||
|
- Mandatory review fields
|
||||||
|
- Media quality standards
|
||||||
|
- Information verification
|
||||||
|
- Source attribution
|
||||||
|
|
||||||
|
### 2. User Trust
|
||||||
|
- Review authenticity checks
|
||||||
|
- Company verification process
|
||||||
|
- Expert contribution validation
|
||||||
|
- Content moderation workflow
|
||||||
|
|
||||||
|
### 3. Data Completeness
|
||||||
|
- Required park information
|
||||||
|
- Ride specification standards
|
||||||
|
- Historical record requirements
|
||||||
|
- Media documentation needs
|
||||||
|
|
||||||
|
## Usage Flows
|
||||||
|
|
||||||
|
### 1. Park Discovery Flow
|
||||||
|
```
|
||||||
|
Search/Browse → Park Selection → Detail View → Related Content
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Review Creation Flow
|
||||||
|
```
|
||||||
|
Experience → Media Upload → Review Draft → Submission → Moderation
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Company Verification Flow
|
||||||
|
```
|
||||||
|
Registration → Documentation → Verification → Profile Access
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Content Moderation Flow
|
||||||
|
```
|
||||||
|
Submission Queue → Review → Action → Notification
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Roadmap
|
||||||
|
|
||||||
|
### Current Phase
|
||||||
|
1. Core Platform
|
||||||
|
- Park/Ride management
|
||||||
|
- Review system
|
||||||
|
- Basic media handling
|
||||||
|
- User authentication
|
||||||
|
|
||||||
|
2. Quality Features
|
||||||
|
- Content moderation
|
||||||
|
- Company verification
|
||||||
|
- Expert system
|
||||||
|
- Media optimization
|
||||||
|
|
||||||
|
### Next Phase
|
||||||
|
1. Community Features
|
||||||
|
- Enhanced profiles
|
||||||
|
- Achievement system
|
||||||
|
- Social interactions
|
||||||
|
- Content collections
|
||||||
|
|
||||||
|
2. Advanced Media
|
||||||
|
- Video support
|
||||||
|
- Virtual tours
|
||||||
|
- 360° views
|
||||||
|
- AR capabilities
|
||||||
|
|
||||||
|
3. Analytics Enhancement
|
||||||
|
- Advanced metrics
|
||||||
|
- Personalization
|
||||||
|
- Trend prediction
|
||||||
|
- Quality scoring
|
||||||
|
|
||||||
|
## Integration Requirements
|
||||||
|
|
||||||
|
### External Systems
|
||||||
|
- Email service integration
|
||||||
|
- Social authentication providers
|
||||||
|
- Geographic data services
|
||||||
|
- Media processing services
|
||||||
|
|
||||||
|
### Internal Systems
|
||||||
|
- WebSocket notifications
|
||||||
|
- Background task processing
|
||||||
|
- Media optimization pipeline
|
||||||
|
- Analytics processing system
|
||||||
|
|
||||||
|
## Compliance Requirements
|
||||||
|
|
||||||
|
### Data Protection
|
||||||
|
- User privacy controls
|
||||||
|
- Data retention policies
|
||||||
|
- Export capabilities
|
||||||
|
- Deletion workflows
|
||||||
|
|
||||||
|
### Accessibility
|
||||||
|
- WCAG compliance
|
||||||
|
- Screen reader support
|
||||||
|
- Keyboard navigation
|
||||||
|
- Color contrast requirements
|
||||||
|
|
||||||
|
### Content Policies
|
||||||
|
- Review guidelines
|
||||||
|
- Media usage rights
|
||||||
|
- Attribution requirements
|
||||||
|
- Moderation standards
|
||||||
306
memory-bank/documentation/Issues.md
Normal file
306
memory-bank/documentation/Issues.md
Normal file
@@ -0,0 +1,306 @@
|
|||||||
|
# Issues and Technical Debt Documentation
|
||||||
|
|
||||||
|
## Known Bugs
|
||||||
|
|
||||||
|
### 1. Data Integrity Issues
|
||||||
|
|
||||||
|
#### Historical Slug Resolution
|
||||||
|
```python
|
||||||
|
# Current Implementation
|
||||||
|
class Park(models.Model):
|
||||||
|
@classmethod
|
||||||
|
def get_by_slug(cls, slug: str):
|
||||||
|
# Issue: Race condition possible between slug check and retrieval
|
||||||
|
# TODO: Implement proper locking or transaction handling
|
||||||
|
try:
|
||||||
|
return cls.objects.get(slug=slug)
|
||||||
|
except cls.DoesNotExist:
|
||||||
|
return cls.objects.get(historical_slugs__slug=slug)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Media File Management
|
||||||
|
```python
|
||||||
|
# Current Issue
|
||||||
|
class MediaHandler:
|
||||||
|
def process_upload(self, file):
|
||||||
|
# Bug: Temporary files not always cleaned up
|
||||||
|
# TODO: Implement proper cleanup in finally block
|
||||||
|
try:
|
||||||
|
process_file(file)
|
||||||
|
except Exception:
|
||||||
|
log_error()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Performance Issues
|
||||||
|
|
||||||
|
#### N+1 Query Patterns
|
||||||
|
```python
|
||||||
|
# Inefficient Queries in Views
|
||||||
|
class ParkDetailView(DetailView):
|
||||||
|
def get_context_data(self):
|
||||||
|
context = super().get_context_data()
|
||||||
|
# Issue: N+1 queries for each ride's reviews
|
||||||
|
context['rides'] = [
|
||||||
|
{
|
||||||
|
'ride': ride,
|
||||||
|
'reviews': ride.reviews.all() # Causes N+1 query
|
||||||
|
}
|
||||||
|
for ride in self.object.rides.all()
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache Invalidation
|
||||||
|
```python
|
||||||
|
# Inconsistent Cache Updates
|
||||||
|
class ReviewManager:
|
||||||
|
def update_stats(self, obj):
|
||||||
|
# Bug: Race condition in cache updates
|
||||||
|
# TODO: Implement atomic cache updates
|
||||||
|
stats = calculate_stats(obj)
|
||||||
|
cache.set(f'{obj}_stats', stats)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Technical Debt
|
||||||
|
|
||||||
|
### 1. Code Organization
|
||||||
|
|
||||||
|
#### Monolithic Views
|
||||||
|
```python
|
||||||
|
# views.py
|
||||||
|
class ParkView(View):
|
||||||
|
def post(self, request, *args, **kwargs):
|
||||||
|
# TODO: Break down into smaller, focused views
|
||||||
|
# Currently handles too many responsibilities:
|
||||||
|
# - Park creation
|
||||||
|
# - Media processing
|
||||||
|
# - Notification sending
|
||||||
|
# - Stats updating
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Duplicate Business Logic
|
||||||
|
```python
|
||||||
|
# Multiple implementations of similar functionality
|
||||||
|
class ParkValidator:
|
||||||
|
def validate_status(self):
|
||||||
|
# TODO: Consolidate with RideValidator.validate_status
|
||||||
|
if self.status not in VALID_STATUSES:
|
||||||
|
raise ValidationError()
|
||||||
|
|
||||||
|
class RideValidator:
|
||||||
|
def validate_status(self):
|
||||||
|
if self.status not in VALID_STATUSES:
|
||||||
|
raise ValidationError()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Infrastructure
|
||||||
|
|
||||||
|
#### Configuration Management
|
||||||
|
```python
|
||||||
|
# settings.py
|
||||||
|
# TODO: Move to environment variables
|
||||||
|
DATABASE_PASSWORD = 'hardcoded_password'
|
||||||
|
API_KEY = 'hardcoded_key'
|
||||||
|
|
||||||
|
# TODO: Implement proper configuration management
|
||||||
|
FEATURE_FLAGS = {
|
||||||
|
'new_review_system': True,
|
||||||
|
'beta_features': False
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Deployment Process
|
||||||
|
```bash
|
||||||
|
# Manual deployment steps
|
||||||
|
# TODO: Automate deployment process
|
||||||
|
ssh server
|
||||||
|
git pull
|
||||||
|
pip install -r requirements.txt
|
||||||
|
python manage.py migrate
|
||||||
|
supervisorctl restart app
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Testing
|
||||||
|
|
||||||
|
#### Test Coverage Gaps
|
||||||
|
```python
|
||||||
|
# Missing test cases for error conditions
|
||||||
|
class ParkTests(TestCase):
|
||||||
|
def test_create_park(self):
|
||||||
|
# Only tests happy path
|
||||||
|
park = Park.objects.create(name='Test Park')
|
||||||
|
self.assertEqual(park.name, 'Test Park')
|
||||||
|
|
||||||
|
# TODO: Add tests for:
|
||||||
|
# - Invalid input handling
|
||||||
|
# - Concurrent modifications
|
||||||
|
# - Edge cases
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Integration Test Debt
|
||||||
|
```python
|
||||||
|
# Brittle integration tests
|
||||||
|
class APITests(TestCase):
|
||||||
|
# TODO: Replace with proper test doubles
|
||||||
|
def setUp(self):
|
||||||
|
# Direct database dependencies
|
||||||
|
self.park = Park.objects.create()
|
||||||
|
# External service calls
|
||||||
|
self.geocoder = RealGeocoder()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enhancement Opportunities
|
||||||
|
|
||||||
|
### 1. Feature Enhancements
|
||||||
|
|
||||||
|
#### Advanced Search
|
||||||
|
```python
|
||||||
|
# Current basic search implementation
|
||||||
|
class ParkSearch:
|
||||||
|
def search(self, query):
|
||||||
|
# TODO: Implement advanced search features:
|
||||||
|
# - Full-text search
|
||||||
|
# - Faceted search
|
||||||
|
# - Geographic search
|
||||||
|
return Park.objects.filter(name__icontains=query)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Review System
|
||||||
|
```python
|
||||||
|
# Basic review functionality
|
||||||
|
class Review(models.Model):
|
||||||
|
# TODO: Enhance with:
|
||||||
|
# - Rich text support
|
||||||
|
# - Media attachments
|
||||||
|
# - Review responses
|
||||||
|
# - Helpful votes
|
||||||
|
rating = models.IntegerField()
|
||||||
|
comment = models.TextField()
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Technical Improvements
|
||||||
|
|
||||||
|
#### API Versioning
|
||||||
|
```python
|
||||||
|
# Current API structure
|
||||||
|
# TODO: Implement proper API versioning
|
||||||
|
urlpatterns = [
|
||||||
|
path('api/parks/', ParkViewSet.as_view()),
|
||||||
|
# Need to support:
|
||||||
|
# - Multiple versions
|
||||||
|
# - Deprecation handling
|
||||||
|
# - Documentation
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Caching Strategy
|
||||||
|
```python
|
||||||
|
# Basic caching
|
||||||
|
# TODO: Implement:
|
||||||
|
# - Multi-layer caching
|
||||||
|
# - Cache warming
|
||||||
|
# - Intelligent invalidation
|
||||||
|
@cache_page(60 * 15)
|
||||||
|
def park_detail(request, slug):
|
||||||
|
return render(request, 'park_detail.html')
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Performance Optimizations
|
||||||
|
|
||||||
|
#### Database Optimization
|
||||||
|
```python
|
||||||
|
# Current database usage
|
||||||
|
# TODO: Implement:
|
||||||
|
# - Connection pooling
|
||||||
|
# - Read replicas
|
||||||
|
# - Query optimization
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'thrillwiki',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Asset Delivery
|
||||||
|
```python
|
||||||
|
# Static file handling
|
||||||
|
# TODO: Implement:
|
||||||
|
# - CDN integration
|
||||||
|
# - Image optimization pipeline
|
||||||
|
# - Responsive images
|
||||||
|
STATIC_URL = '/static/'
|
||||||
|
MEDIA_URL = '/media/'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prioritized Improvements
|
||||||
|
|
||||||
|
### High Priority
|
||||||
|
1. Security Fixes
|
||||||
|
- Fix authentication vulnerabilities
|
||||||
|
- Implement proper input validation
|
||||||
|
- Secure file uploads
|
||||||
|
|
||||||
|
2. Critical Performance Issues
|
||||||
|
- Resolve N+1 queries
|
||||||
|
- Implement connection pooling
|
||||||
|
- Optimize cache usage
|
||||||
|
|
||||||
|
3. Data Integrity
|
||||||
|
- Fix race conditions
|
||||||
|
- Implement proper transactions
|
||||||
|
- Add data validation
|
||||||
|
|
||||||
|
### Medium Priority
|
||||||
|
1. Technical Debt
|
||||||
|
- Refactor monolithic views
|
||||||
|
- Consolidate duplicate code
|
||||||
|
- Improve test coverage
|
||||||
|
|
||||||
|
2. Developer Experience
|
||||||
|
- Automate deployment
|
||||||
|
- Improve documentation
|
||||||
|
- Add development tools
|
||||||
|
|
||||||
|
3. Feature Enhancements
|
||||||
|
- Implement advanced search
|
||||||
|
- Enhance review system
|
||||||
|
- Add API versioning
|
||||||
|
|
||||||
|
### Low Priority
|
||||||
|
1. Nice-to-have Features
|
||||||
|
- Rich text support
|
||||||
|
- Enhanced media handling
|
||||||
|
- Social features
|
||||||
|
|
||||||
|
2. Infrastructure Improvements
|
||||||
|
- CDN integration
|
||||||
|
- Monitoring enhancements
|
||||||
|
- Analytics improvements
|
||||||
|
|
||||||
|
## Implementation Plan
|
||||||
|
|
||||||
|
### Phase 1: Critical Fixes
|
||||||
|
```python
|
||||||
|
# Timeline: Q1 2024
|
||||||
|
# Focus:
|
||||||
|
# - Security vulnerabilities
|
||||||
|
# - Performance bottlenecks
|
||||||
|
# - Data integrity issues
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 2: Technical Debt
|
||||||
|
```python
|
||||||
|
# Timeline: Q2 2024
|
||||||
|
# Focus:
|
||||||
|
# - Code refactoring
|
||||||
|
# - Test coverage
|
||||||
|
# - Documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Phase 3: Enhancements
|
||||||
|
```python
|
||||||
|
# Timeline: Q3-Q4 2024
|
||||||
|
# Focus:
|
||||||
|
# - Feature improvements
|
||||||
|
# - Infrastructure upgrades
|
||||||
|
# - User experience
|
||||||
388
memory-bank/documentation/Performance.md
Normal file
388
memory-bank/documentation/Performance.md
Normal file
@@ -0,0 +1,388 @@
|
|||||||
|
# Performance Documentation
|
||||||
|
|
||||||
|
## Performance Architecture
|
||||||
|
|
||||||
|
### Caching Strategy
|
||||||
|
|
||||||
|
#### Cache Layers
|
||||||
|
```python
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
|
||||||
|
'LOCATION': 'redis://127.0.0.1:6379/1',
|
||||||
|
'OPTIONS': {
|
||||||
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
|
'PARSER_CLASS': 'redis.connection.HiredisParser',
|
||||||
|
'CONNECTION_POOL_CLASS': 'redis.BlockingConnectionPool',
|
||||||
|
'CONNECTION_POOL_CLASS_KWARGS': {
|
||||||
|
'max_connections': 50,
|
||||||
|
'timeout': 20,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache Patterns
|
||||||
|
```python
|
||||||
|
# View caching
|
||||||
|
@method_decorator(cache_page(60 * 15))
|
||||||
|
def park_list(request):
|
||||||
|
parks = Park.objects.all()
|
||||||
|
return render(request, 'parks/list.html', {'parks': parks})
|
||||||
|
|
||||||
|
# Template fragment caching
|
||||||
|
{% load cache %}
|
||||||
|
{% cache 300 park_detail park.id %}
|
||||||
|
... expensive template logic ...
|
||||||
|
{% endcache %}
|
||||||
|
|
||||||
|
# Low-level cache API
|
||||||
|
def get_park_stats(park_id):
|
||||||
|
cache_key = f'park_stats:{park_id}'
|
||||||
|
stats = cache.get(cache_key)
|
||||||
|
if stats is None:
|
||||||
|
stats = calculate_park_stats(park_id)
|
||||||
|
cache.set(cache_key, stats, timeout=3600)
|
||||||
|
return stats
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Optimization
|
||||||
|
|
||||||
|
#### Query Optimization
|
||||||
|
```python
|
||||||
|
# Efficient querying patterns
|
||||||
|
class ParkQuerySet(models.QuerySet):
|
||||||
|
def with_stats(self):
|
||||||
|
return self.annotate(
|
||||||
|
ride_count=Count('rides'),
|
||||||
|
avg_rating=Avg('reviews__rating')
|
||||||
|
).select_related('owner')\
|
||||||
|
.prefetch_related('rides', 'areas')
|
||||||
|
|
||||||
|
# Indexes
|
||||||
|
class Park(models.Model):
|
||||||
|
class Meta:
|
||||||
|
indexes = [
|
||||||
|
models.Index(fields=['slug']),
|
||||||
|
models.Index(fields=['status', 'created_at']),
|
||||||
|
models.Index(fields=['location_id', 'status'])
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Database Configuration
|
||||||
|
```python
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'thrillwiki',
|
||||||
|
'CONN_MAX_AGE': 60,
|
||||||
|
'OPTIONS': {
|
||||||
|
'statement_timeout': 3000,
|
||||||
|
'idle_in_transaction_timeout': 3000,
|
||||||
|
},
|
||||||
|
'ATOMIC_REQUESTS': False,
|
||||||
|
'CONN_HEALTH_CHECKS': True,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Asset Optimization
|
||||||
|
|
||||||
|
#### Static File Handling
|
||||||
|
```python
|
||||||
|
# WhiteNoise configuration
|
||||||
|
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
|
||||||
|
|
||||||
|
WHITENOISE_OPTIONS = {
|
||||||
|
'allow_all_origins': False,
|
||||||
|
'max_age': 31536000, # 1 year
|
||||||
|
'compression_enabled': True,
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Media Optimization
|
||||||
|
```python
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
def optimize_image(image_path):
|
||||||
|
with Image.open(image_path) as img:
|
||||||
|
# Convert to WebP
|
||||||
|
webp_path = f"{os.path.splitext(image_path)[0]}.webp"
|
||||||
|
img.save(webp_path, 'WebP', quality=85, method=6)
|
||||||
|
|
||||||
|
# Create thumbnails
|
||||||
|
sizes = [(800, 600), (400, 300)]
|
||||||
|
for size in sizes:
|
||||||
|
thumb = img.copy()
|
||||||
|
thumb.thumbnail(size)
|
||||||
|
thumb_path = f"{os.path.splitext(image_path)[0]}_{size[0]}x{size[1]}.webp"
|
||||||
|
thumb.save(thumb_path, 'WebP', quality=85, method=6)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Monitoring
|
||||||
|
|
||||||
|
### Application Monitoring
|
||||||
|
|
||||||
|
#### APM Configuration
|
||||||
|
```python
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django_prometheus.middleware.PrometheusBeforeMiddleware',
|
||||||
|
# ... other middleware ...
|
||||||
|
'django_prometheus.middleware.PrometheusAfterMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
PROMETHEUS_METRICS = {
|
||||||
|
'scrape_interval': 15,
|
||||||
|
'namespace': 'thrillwiki',
|
||||||
|
'metrics_path': '/metrics',
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Custom Metrics
|
||||||
|
```python
|
||||||
|
from prometheus_client import Counter, Histogram
|
||||||
|
|
||||||
|
# Request metrics
|
||||||
|
http_requests_total = Counter(
|
||||||
|
'http_requests_total',
|
||||||
|
'Total HTTP requests',
|
||||||
|
['method', 'endpoint', 'status']
|
||||||
|
)
|
||||||
|
|
||||||
|
# Response time metrics
|
||||||
|
response_time = Histogram(
|
||||||
|
'response_time_seconds',
|
||||||
|
'Response time in seconds',
|
||||||
|
['endpoint']
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Logging
|
||||||
|
|
||||||
|
#### Logging Configuration
|
||||||
|
```python
|
||||||
|
LOGGING = {
|
||||||
|
'handlers': {
|
||||||
|
'performance': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'class': 'logging.handlers.TimedRotatingFileHandler',
|
||||||
|
'filename': 'logs/performance.log',
|
||||||
|
'when': 'midnight',
|
||||||
|
'interval': 1,
|
||||||
|
'backupCount': 30,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'loggers': {
|
||||||
|
'performance': {
|
||||||
|
'handlers': ['performance'],
|
||||||
|
'level': 'INFO',
|
||||||
|
'propagate': False,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Performance Logging Middleware
|
||||||
|
```python
|
||||||
|
class PerformanceMiddleware:
|
||||||
|
def __init__(self, get_response):
|
||||||
|
self.get_response = get_response
|
||||||
|
self.logger = logging.getLogger('performance')
|
||||||
|
|
||||||
|
def __call__(self, request):
|
||||||
|
start_time = time.time()
|
||||||
|
response = self.get_response(request)
|
||||||
|
duration = time.time() - start_time
|
||||||
|
|
||||||
|
self.logger.info({
|
||||||
|
'path': request.path,
|
||||||
|
'method': request.method,
|
||||||
|
'duration': duration,
|
||||||
|
'status': response.status_code
|
||||||
|
})
|
||||||
|
|
||||||
|
return response
|
||||||
|
```
|
||||||
|
|
||||||
|
## Scaling Strategy
|
||||||
|
|
||||||
|
### Application Scaling
|
||||||
|
|
||||||
|
#### Asynchronous Tasks
|
||||||
|
```python
|
||||||
|
# Celery configuration
|
||||||
|
CELERY_BROKER_URL = 'redis://localhost:6379/2'
|
||||||
|
CELERY_RESULT_BACKEND = 'redis://localhost:6379/3'
|
||||||
|
|
||||||
|
CELERY_TASK_ROUTES = {
|
||||||
|
'media.tasks.process_image': {'queue': 'media'},
|
||||||
|
'analytics.tasks.update_stats': {'queue': 'analytics'},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Task definition
|
||||||
|
@shared_task(rate_limit='100/m')
|
||||||
|
def process_image(image_id):
|
||||||
|
image = Image.objects.get(id=image_id)
|
||||||
|
optimize_image(image.file.path)
|
||||||
|
create_thumbnails(image)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Load Balancing
|
||||||
|
```nginx
|
||||||
|
# Nginx configuration
|
||||||
|
upstream thrillwiki {
|
||||||
|
least_conn; # Least connections algorithm
|
||||||
|
server backend1.thrillwiki.com:8000;
|
||||||
|
server backend2.thrillwiki.com:8000;
|
||||||
|
server backend3.thrillwiki.com:8000;
|
||||||
|
|
||||||
|
keepalive 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name thrillwiki.com;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
proxy_pass http://thrillwiki;
|
||||||
|
proxy_http_version 1.1;
|
||||||
|
proxy_set_header Connection "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Database Scaling
|
||||||
|
|
||||||
|
#### Read Replicas
|
||||||
|
```python
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'thrillwiki',
|
||||||
|
# Primary DB configuration
|
||||||
|
},
|
||||||
|
'replica1': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'NAME': 'thrillwiki',
|
||||||
|
# Read replica configuration
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DATABASE_ROUTERS = ['core.db.PrimaryReplicaRouter']
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Connection Pooling
|
||||||
|
```python
|
||||||
|
# Django DB configuration with PgBouncer
|
||||||
|
DATABASES = {
|
||||||
|
'default': {
|
||||||
|
'ENGINE': 'django.db.backends.postgresql',
|
||||||
|
'OPTIONS': {
|
||||||
|
'application_name': 'thrillwiki',
|
||||||
|
'max_prepared_transactions': 0,
|
||||||
|
},
|
||||||
|
'POOL_OPTIONS': {
|
||||||
|
'POOL_SIZE': 20,
|
||||||
|
'MAX_OVERFLOW': 10,
|
||||||
|
'RECYCLE': 300,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Caching Strategy
|
||||||
|
|
||||||
|
#### Multi-layer Caching
|
||||||
|
```python
|
||||||
|
# Cache configuration with fallback
|
||||||
|
CACHES = {
|
||||||
|
'default': {
|
||||||
|
'BACKEND': 'django_redis.cache.RedisCache',
|
||||||
|
'LOCATION': 'redis://primary:6379/1',
|
||||||
|
'OPTIONS': {
|
||||||
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
|
'MASTER_CACHE': True,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'replica': {
|
||||||
|
'BACKEND': 'django_redis.cache.RedisCache',
|
||||||
|
'LOCATION': 'redis://replica:6379/1',
|
||||||
|
'OPTIONS': {
|
||||||
|
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Cache Invalidation
|
||||||
|
```python
|
||||||
|
class CacheInvalidationMixin:
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
# Invalidate related caches
|
||||||
|
cache_keys = self.get_cache_keys()
|
||||||
|
cache.delete_many(cache_keys)
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def get_cache_keys(self):
|
||||||
|
# Return list of related cache keys
|
||||||
|
return [
|
||||||
|
f'park:{self.pk}',
|
||||||
|
f'park_stats:{self.pk}',
|
||||||
|
'park_list'
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Bottlenecks
|
||||||
|
|
||||||
|
### Known Issues
|
||||||
|
|
||||||
|
1. N+1 Query Patterns
|
||||||
|
```python
|
||||||
|
# Bad pattern
|
||||||
|
for park in Park.objects.all():
|
||||||
|
print(park.rides.count()) # Causes N+1 queries
|
||||||
|
|
||||||
|
# Solution
|
||||||
|
parks = Park.objects.annotate(
|
||||||
|
ride_count=Count('rides')
|
||||||
|
).all()
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Memory Leaks
|
||||||
|
```python
|
||||||
|
# Memory leak in long-running tasks
|
||||||
|
class LongRunningTask:
|
||||||
|
def __init__(self):
|
||||||
|
self.cache = {}
|
||||||
|
|
||||||
|
def process(self, items):
|
||||||
|
# Clear cache periodically
|
||||||
|
if len(self.cache) > 1000:
|
||||||
|
self.cache.clear()
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Tips
|
||||||
|
|
||||||
|
1. Query Optimization
|
||||||
|
```python
|
||||||
|
# Use exists() for checking existence
|
||||||
|
if Park.objects.filter(slug=slug).exists():
|
||||||
|
# Do something
|
||||||
|
|
||||||
|
# Use values() for simple data
|
||||||
|
parks = Park.objects.values('id', 'name')
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Bulk Operations
|
||||||
|
```python
|
||||||
|
# Use bulk create
|
||||||
|
Park.objects.bulk_create([
|
||||||
|
Park(name='Park 1'),
|
||||||
|
Park(name='Park 2')
|
||||||
|
])
|
||||||
|
|
||||||
|
# Use bulk update
|
||||||
|
Park.objects.filter(status='CLOSED').update(
|
||||||
|
status='OPERATING'
|
||||||
|
)
|
||||||
339
memory-bank/documentation/Security.md
Normal file
339
memory-bank/documentation/Security.md
Normal file
@@ -0,0 +1,339 @@
|
|||||||
|
# Security Documentation
|
||||||
|
|
||||||
|
## Authentication System
|
||||||
|
|
||||||
|
### Authentication Stack
|
||||||
|
```python
|
||||||
|
# Settings configuration
|
||||||
|
AUTHENTICATION_BACKENDS = [
|
||||||
|
'django.contrib.auth.backends.ModelBackend',
|
||||||
|
'allauth.account.auth_backends.AuthenticationBackend',
|
||||||
|
]
|
||||||
|
|
||||||
|
INSTALLED_APPS = [
|
||||||
|
'django.contrib.auth',
|
||||||
|
'django.contrib.sessions',
|
||||||
|
'allauth',
|
||||||
|
'allauth.account',
|
||||||
|
'allauth.socialaccount',
|
||||||
|
'oauth2_provider',
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
User->>+Server: Login Request
|
||||||
|
Server->>+Auth Service: Validate Credentials
|
||||||
|
Auth Service->>+Database: Check User
|
||||||
|
Database-->>-Auth Service: User Data
|
||||||
|
Auth Service-->>-Server: Auth Token
|
||||||
|
Server-->>-User: Session Cookie
|
||||||
|
```
|
||||||
|
|
||||||
|
## Authorization Framework
|
||||||
|
|
||||||
|
### Permission System
|
||||||
|
|
||||||
|
#### Model Permissions
|
||||||
|
```python
|
||||||
|
class Park(models.Model):
|
||||||
|
class Meta:
|
||||||
|
permissions = [
|
||||||
|
("can_publish_park", "Can publish park"),
|
||||||
|
("can_moderate_park", "Can moderate park"),
|
||||||
|
("can_verify_park", "Can verify park information"),
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### View Permissions
|
||||||
|
```python
|
||||||
|
class ModeratedCreateView(LoginRequiredMixin, PermissionRequiredMixin):
|
||||||
|
permission_required = 'parks.can_publish_park'
|
||||||
|
raise_exception = True
|
||||||
|
```
|
||||||
|
|
||||||
|
### Role-Based Access Control
|
||||||
|
|
||||||
|
#### User Groups
|
||||||
|
1. Administrators
|
||||||
|
- Full system access
|
||||||
|
- Configuration management
|
||||||
|
- User management
|
||||||
|
|
||||||
|
2. Moderators
|
||||||
|
- Content moderation
|
||||||
|
- User management
|
||||||
|
- Report handling
|
||||||
|
|
||||||
|
3. Company Representatives
|
||||||
|
- Company profile management
|
||||||
|
- Official updates
|
||||||
|
- Response management
|
||||||
|
|
||||||
|
4. Regular Users
|
||||||
|
- Content creation
|
||||||
|
- Review submission
|
||||||
|
- Media uploads
|
||||||
|
|
||||||
|
#### Permission Matrix
|
||||||
|
```python
|
||||||
|
ROLE_PERMISSIONS = {
|
||||||
|
'administrator': [
|
||||||
|
'can_manage_users',
|
||||||
|
'can_configure_system',
|
||||||
|
'can_moderate_content',
|
||||||
|
],
|
||||||
|
'moderator': [
|
||||||
|
'can_moderate_content',
|
||||||
|
'can_manage_reports',
|
||||||
|
'can_verify_information',
|
||||||
|
],
|
||||||
|
'company_rep': [
|
||||||
|
'can_manage_company',
|
||||||
|
'can_post_updates',
|
||||||
|
'can_respond_reviews',
|
||||||
|
],
|
||||||
|
'user': [
|
||||||
|
'can_create_content',
|
||||||
|
'can_submit_reviews',
|
||||||
|
'can_upload_media',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Controls
|
||||||
|
|
||||||
|
### Request Security
|
||||||
|
|
||||||
|
#### CSRF Protection
|
||||||
|
```python
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.csrf.CsrfViewMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
# Template configuration
|
||||||
|
{% csrf_token %}
|
||||||
|
|
||||||
|
# AJAX request handling
|
||||||
|
headers: {
|
||||||
|
'X-CSRFToken': getCookie('csrftoken')
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### XSS Prevention
|
||||||
|
```python
|
||||||
|
# Template autoescape
|
||||||
|
{% autoescape on %}
|
||||||
|
{{ user_content }}
|
||||||
|
{% endautoescape %}
|
||||||
|
|
||||||
|
# Content Security Policy
|
||||||
|
CSP_DEFAULT_SRC = ("'self'",)
|
||||||
|
CSP_SCRIPT_SRC = ("'self'",)
|
||||||
|
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
|
||||||
|
CSP_IMG_SRC = ("'self'", "data:", "https:")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Data Protection
|
||||||
|
|
||||||
|
#### Password Security
|
||||||
|
```python
|
||||||
|
# Password validation
|
||||||
|
AUTH_PASSWORD_VALIDATORS = [
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||||
|
'OPTIONS': {
|
||||||
|
'min_length': 12,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||||
|
},
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Data Encryption
|
||||||
|
```python
|
||||||
|
# Database encryption
|
||||||
|
ENCRYPTED_FIELDS = {
|
||||||
|
'fields': {
|
||||||
|
'users.User.ssn': 'django_cryptography.fields.encrypt',
|
||||||
|
'payment.Card.number': 'django_cryptography.fields.encrypt',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# File encryption
|
||||||
|
ENCRYPTED_FILE_STORAGE = 'django_cryptography.storage.EncryptedFileSystemStorage'
|
||||||
|
```
|
||||||
|
|
||||||
|
### Session Security
|
||||||
|
|
||||||
|
#### Session Configuration
|
||||||
|
```python
|
||||||
|
# Session settings
|
||||||
|
SESSION_COOKIE_SECURE = True
|
||||||
|
SESSION_COOKIE_HTTPONLY = True
|
||||||
|
SESSION_COOKIE_SAMESITE = 'Lax'
|
||||||
|
SESSION_ENGINE = 'django.contrib.sessions.backends.cached_db'
|
||||||
|
SESSION_EXPIRE_AT_BROWSER_CLOSE = True
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Session Management
|
||||||
|
```python
|
||||||
|
# Session cleanup
|
||||||
|
CELERYBEAT_SCHEDULE = {
|
||||||
|
'cleanup-expired-sessions': {
|
||||||
|
'task': 'core.tasks.cleanup_expired_sessions',
|
||||||
|
'schedule': crontab(hour=4, minute=0)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## API Security
|
||||||
|
|
||||||
|
### Authentication
|
||||||
|
```python
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_AUTHENTICATION_CLASSES': [
|
||||||
|
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
|
||||||
|
'rest_framework.authentication.SessionAuthentication',
|
||||||
|
],
|
||||||
|
'DEFAULT_PERMISSION_CLASSES': [
|
||||||
|
'rest_framework.permissions.IsAuthenticated',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Rate Limiting
|
||||||
|
```python
|
||||||
|
# Rate limiting configuration
|
||||||
|
REST_FRAMEWORK = {
|
||||||
|
'DEFAULT_THROTTLE_CLASSES': [
|
||||||
|
'rest_framework.throttling.AnonRateThrottle',
|
||||||
|
'rest_framework.throttling.UserRateThrottle'
|
||||||
|
],
|
||||||
|
'DEFAULT_THROTTLE_RATES': {
|
||||||
|
'anon': '100/day',
|
||||||
|
'user': '1000/day'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Headers
|
||||||
|
|
||||||
|
### HTTP Security Headers
|
||||||
|
```python
|
||||||
|
MIDDLEWARE = [
|
||||||
|
'django.middleware.security.SecurityMiddleware',
|
||||||
|
]
|
||||||
|
|
||||||
|
SECURE_HSTS_SECONDS = 31536000
|
||||||
|
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
|
||||||
|
SECURE_HSTS_PRELOAD = True
|
||||||
|
SECURE_SSL_REDIRECT = True
|
||||||
|
SECURE_REFERRER_POLICY = 'same-origin'
|
||||||
|
SECURE_BROWSER_XSS_FILTER = True
|
||||||
|
```
|
||||||
|
|
||||||
|
## File Upload Security
|
||||||
|
|
||||||
|
### Upload Configuration
|
||||||
|
```python
|
||||||
|
# File upload settings
|
||||||
|
FILE_UPLOAD_MAX_MEMORY_SIZE = 2621440 # 2.5 MB
|
||||||
|
FILE_UPLOAD_PERMISSIONS = 0o644
|
||||||
|
ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif']
|
||||||
|
|
||||||
|
def validate_file_extension(value):
|
||||||
|
ext = os.path.splitext(value.name)[1]
|
||||||
|
if not ext.lower() in ALLOWED_EXTENSIONS:
|
||||||
|
raise ValidationError('Unsupported file extension.')
|
||||||
|
```
|
||||||
|
|
||||||
|
### Media Security
|
||||||
|
```python
|
||||||
|
# Serve media files securely
|
||||||
|
@login_required
|
||||||
|
def serve_protected_file(request, path):
|
||||||
|
if not request.user.has_perm('can_access_file'):
|
||||||
|
raise PermissionDenied
|
||||||
|
response = serve(request, path, document_root=settings.MEDIA_ROOT)
|
||||||
|
response['Content-Disposition'] = 'attachment'
|
||||||
|
return response
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Monitoring
|
||||||
|
|
||||||
|
### Audit Logging
|
||||||
|
```python
|
||||||
|
# Audit log configuration
|
||||||
|
AUDIT_LOG_HANDLERS = {
|
||||||
|
'security': {
|
||||||
|
'level': 'INFO',
|
||||||
|
'class': 'logging.handlers.RotatingFileHandler',
|
||||||
|
'filename': 'logs/security.log',
|
||||||
|
'maxBytes': 1024*1024*5, # 5 MB
|
||||||
|
'backupCount': 5,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
# Audit log usage
|
||||||
|
def log_security_event(event_type, user, details):
|
||||||
|
logger.info(f'Security event: {event_type}', extra={
|
||||||
|
'user_id': user.id,
|
||||||
|
'ip_address': get_client_ip(request),
|
||||||
|
'details': details
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
### Security Alerts
|
||||||
|
```python
|
||||||
|
# Alert configuration
|
||||||
|
SECURITY_ALERTS = {
|
||||||
|
'login_attempts': {
|
||||||
|
'threshold': 5,
|
||||||
|
'window': 300, # 5 minutes
|
||||||
|
'action': 'account_lock'
|
||||||
|
},
|
||||||
|
'api_errors': {
|
||||||
|
'threshold': 100,
|
||||||
|
'window': 3600, # 1 hour
|
||||||
|
'action': 'notify_admin'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Incident Response
|
||||||
|
|
||||||
|
### Security Incident Workflow
|
||||||
|
1. Detection
|
||||||
|
2. Analysis
|
||||||
|
3. Containment
|
||||||
|
4. Eradication
|
||||||
|
5. Recovery
|
||||||
|
6. Lessons Learned
|
||||||
|
|
||||||
|
### Response Actions
|
||||||
|
```python
|
||||||
|
class SecurityIncident:
|
||||||
|
def contain_threat(self):
|
||||||
|
# Lock affected accounts
|
||||||
|
# Block suspicious IPs
|
||||||
|
# Disable compromised tokens
|
||||||
|
|
||||||
|
def investigate(self):
|
||||||
|
# Collect logs
|
||||||
|
# Analyze patterns
|
||||||
|
# Document findings
|
||||||
|
|
||||||
|
def recover(self):
|
||||||
|
# Restore systems
|
||||||
|
# Reset credentials
|
||||||
|
# Update security controls
|
||||||
350
memory-bank/documentation/Testing.md
Normal file
350
memory-bank/documentation/Testing.md
Normal file
@@ -0,0 +1,350 @@
|
|||||||
|
# Testing Documentation
|
||||||
|
|
||||||
|
## Testing Architecture
|
||||||
|
|
||||||
|
### Test Organization
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── unit/
|
||||||
|
│ ├── test_models.py
|
||||||
|
│ ├── test_views.py
|
||||||
|
│ └── test_forms.py
|
||||||
|
├── integration/
|
||||||
|
│ ├── test_workflows.py
|
||||||
|
│ └── test_apis.py
|
||||||
|
└── e2e/
|
||||||
|
└── test_user_journeys.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Configuration
|
||||||
|
```python
|
||||||
|
# pytest configuration
|
||||||
|
pytest_plugins = [
|
||||||
|
"tests.fixtures.parks",
|
||||||
|
"tests.fixtures.users",
|
||||||
|
"tests.fixtures.media"
|
||||||
|
]
|
||||||
|
|
||||||
|
# Test settings
|
||||||
|
TEST_RUNNER = 'django.test.runner.DiscoverRunner'
|
||||||
|
TEST_MODE = True
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Types
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
#### Model Tests
|
||||||
|
```python
|
||||||
|
class ParkModelTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.park = Park.objects.create(
|
||||||
|
name="Test Park",
|
||||||
|
status="OPERATING"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_slug_generation(self):
|
||||||
|
self.assertEqual(self.park.slug, "test-park")
|
||||||
|
|
||||||
|
def test_status_validation(self):
|
||||||
|
with self.assertRaises(ValidationError):
|
||||||
|
Park.objects.create(
|
||||||
|
name="Invalid Park",
|
||||||
|
status="INVALID"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
#### View Tests
|
||||||
|
```python
|
||||||
|
class ParkViewTest(TestCase):
|
||||||
|
def setUp(self):
|
||||||
|
self.client = Client()
|
||||||
|
self.user = User.objects.create_user(
|
||||||
|
username="testuser",
|
||||||
|
[PASSWORD-REMOVED]"
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_park_list_view(self):
|
||||||
|
response = self.client.get(reverse('parks:list'))
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
self.assertTemplateUsed(response, 'parks/park_list.html')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Form Tests
|
||||||
|
```python
|
||||||
|
class RideFormTest(TestCase):
|
||||||
|
def test_valid_form(self):
|
||||||
|
form = RideForm({
|
||||||
|
'name': 'Test Ride',
|
||||||
|
'status': 'OPERATING',
|
||||||
|
'height_requirement': 48
|
||||||
|
})
|
||||||
|
self.assertTrue(form.is_valid())
|
||||||
|
```
|
||||||
|
|
||||||
|
### Integration Tests
|
||||||
|
|
||||||
|
#### Workflow Tests
|
||||||
|
```python
|
||||||
|
class ReviewWorkflowTest(TestCase):
|
||||||
|
def test_review_moderation_flow(self):
|
||||||
|
# Create review
|
||||||
|
review = self.create_review()
|
||||||
|
|
||||||
|
# Submit for moderation
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('reviews:submit_moderation',
|
||||||
|
kwargs={'pk': review.pk})
|
||||||
|
)
|
||||||
|
self.assertEqual(review.refresh_from_db().status, 'PENDING')
|
||||||
|
|
||||||
|
# Approve review
|
||||||
|
moderator = self.create_moderator()
|
||||||
|
self.client.force_login(moderator)
|
||||||
|
response = self.client.post(
|
||||||
|
reverse('reviews:approve',
|
||||||
|
kwargs={'pk': review.pk})
|
||||||
|
)
|
||||||
|
self.assertEqual(review.refresh_from_db().status, 'APPROVED')
|
||||||
|
```
|
||||||
|
|
||||||
|
#### API Tests
|
||||||
|
```python
|
||||||
|
class ParkAPITest(APITestCase):
|
||||||
|
def test_park_list_api(self):
|
||||||
|
url = reverse('api:park-list')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, 200)
|
||||||
|
|
||||||
|
def test_park_create_api(self):
|
||||||
|
url = reverse('api:park-create')
|
||||||
|
data = {
|
||||||
|
'name': 'New Park',
|
||||||
|
'status': 'OPERATING'
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, 201)
|
||||||
|
```
|
||||||
|
|
||||||
|
### End-to-End Tests
|
||||||
|
|
||||||
|
#### User Journey Tests
|
||||||
|
```python
|
||||||
|
class UserJourneyTest(LiveServerTestCase):
|
||||||
|
def test_park_review_journey(self):
|
||||||
|
# User logs in
|
||||||
|
self.login_user()
|
||||||
|
|
||||||
|
# Navigate to park
|
||||||
|
self.browser.get(f'{self.live_server_url}/parks/test-park/')
|
||||||
|
|
||||||
|
# Create review
|
||||||
|
self.browser.find_element_by_id('write-review').click()
|
||||||
|
self.browser.find_element_by_id('review-text').send_keys('Great park!')
|
||||||
|
self.browser.find_element_by_id('submit').click()
|
||||||
|
|
||||||
|
# Verify review appears
|
||||||
|
review_element = self.browser.find_element_by_class_name('review-item')
|
||||||
|
self.assertIn('Great park!', review_element.text)
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Pipeline
|
||||||
|
|
||||||
|
### GitHub Actions Configuration
|
||||||
|
```yaml
|
||||||
|
name: ThrillWiki CI
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
pull_request:
|
||||||
|
branches: [ main, develop ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
services:
|
||||||
|
postgres:
|
||||||
|
image: postgres:13
|
||||||
|
env:
|
||||||
|
POSTGRES_PASSWORD: postgres
|
||||||
|
options: >-
|
||||||
|
--health-cmd pg_isready
|
||||||
|
--health-interval 10s
|
||||||
|
--health-timeout 5s
|
||||||
|
--health-retries 5
|
||||||
|
ports:
|
||||||
|
- 5432:5432
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up Python
|
||||||
|
uses: actions/setup-python@v2
|
||||||
|
with:
|
||||||
|
python-version: '3.11'
|
||||||
|
|
||||||
|
- name: Install Dependencies
|
||||||
|
run: |
|
||||||
|
python -m pip install --upgrade pip
|
||||||
|
pip install -r requirements.txt
|
||||||
|
|
||||||
|
- name: Run Tests
|
||||||
|
env:
|
||||||
|
DATABASE_URL: postgres://postgres:postgres@localhost:5432/thrillwiki_test
|
||||||
|
run: |
|
||||||
|
pytest --cov=./ --cov-report=xml
|
||||||
|
|
||||||
|
- name: Upload Coverage
|
||||||
|
uses: codecov/codecov-action@v1
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quality Metrics
|
||||||
|
|
||||||
|
### Code Coverage
|
||||||
|
```python
|
||||||
|
# Coverage configuration
|
||||||
|
[coverage:run]
|
||||||
|
source = .
|
||||||
|
omit =
|
||||||
|
*/migrations/*
|
||||||
|
*/tests/*
|
||||||
|
manage.py
|
||||||
|
|
||||||
|
[coverage:report]
|
||||||
|
exclude_lines =
|
||||||
|
pragma: no cover
|
||||||
|
def __str__
|
||||||
|
raise NotImplementedError
|
||||||
|
```
|
||||||
|
|
||||||
|
### Code Quality Tools
|
||||||
|
```python
|
||||||
|
# flake8 configuration
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 88
|
||||||
|
extend-ignore = E203
|
||||||
|
exclude = .git,__pycache__,build,dist
|
||||||
|
|
||||||
|
# black configuration
|
||||||
|
[tool.black]
|
||||||
|
line-length = 88
|
||||||
|
target-version = ['py311']
|
||||||
|
include = '\.pyi?$'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Data Management
|
||||||
|
|
||||||
|
### Fixtures
|
||||||
|
```python
|
||||||
|
# fixtures/parks.json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"model": "parks.park",
|
||||||
|
"pk": 1,
|
||||||
|
"fields": {
|
||||||
|
"name": "Test Park",
|
||||||
|
"slug": "test-park",
|
||||||
|
"status": "OPERATING"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Factory Classes
|
||||||
|
```python
|
||||||
|
from factory.django import DjangoModelFactory
|
||||||
|
|
||||||
|
class ParkFactory(DjangoModelFactory):
|
||||||
|
class Meta:
|
||||||
|
model = Park
|
||||||
|
|
||||||
|
name = factory.Sequence(lambda n: f'Test Park {n}')
|
||||||
|
status = 'OPERATING'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
### Load Testing
|
||||||
|
```python
|
||||||
|
from locust import HttpUser, task, between
|
||||||
|
|
||||||
|
class ParkUser(HttpUser):
|
||||||
|
wait_time = between(1, 3)
|
||||||
|
|
||||||
|
@task
|
||||||
|
def view_park_list(self):
|
||||||
|
self.client.get("/parks/")
|
||||||
|
|
||||||
|
@task
|
||||||
|
def view_park_detail(self):
|
||||||
|
self.client.get("/parks/test-park/")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Benchmark Tests
|
||||||
|
```python
|
||||||
|
class ParkBenchmarkTest(TestCase):
|
||||||
|
def test_park_list_performance(self):
|
||||||
|
start_time = time.time()
|
||||||
|
Park.objects.all().select_related('owner')
|
||||||
|
end_time = time.time()
|
||||||
|
|
||||||
|
self.assertLess(end_time - start_time, 0.1)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Automation
|
||||||
|
|
||||||
|
### Test Runner Configuration
|
||||||
|
```python
|
||||||
|
# Custom test runner
|
||||||
|
class CustomTestRunner(DiscoverRunner):
|
||||||
|
def setup_databases(self, **kwargs):
|
||||||
|
# Custom database setup
|
||||||
|
return super().setup_databases(**kwargs)
|
||||||
|
|
||||||
|
def teardown_databases(self, old_config, **kwargs):
|
||||||
|
# Custom cleanup
|
||||||
|
return super().teardown_databases(old_config, **kwargs)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Automated Test Execution
|
||||||
|
```bash
|
||||||
|
# Test execution script
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Run unit tests
|
||||||
|
pytest tests/unit/
|
||||||
|
|
||||||
|
# Run integration tests
|
||||||
|
pytest tests/integration/
|
||||||
|
|
||||||
|
# Run e2e tests
|
||||||
|
pytest tests/e2e/
|
||||||
|
|
||||||
|
# Generate coverage report
|
||||||
|
coverage run -m pytest
|
||||||
|
coverage report
|
||||||
|
coverage html
|
||||||
|
```
|
||||||
|
|
||||||
|
## Monitoring and Reporting
|
||||||
|
|
||||||
|
### Test Reports
|
||||||
|
```python
|
||||||
|
# pytest-html configuration
|
||||||
|
pytest_html_report_title = "ThrillWiki Test Report"
|
||||||
|
|
||||||
|
def pytest_html_report_data(report):
|
||||||
|
report.description = "Test Results for ThrillWiki"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Coverage Reports
|
||||||
|
```python
|
||||||
|
# Coverage reporting configuration
|
||||||
|
COVERAGE_REPORT_OPTIONS = {
|
||||||
|
'report_type': 'html',
|
||||||
|
'directory': 'coverage_html',
|
||||||
|
'title': 'ThrillWiki Coverage Report',
|
||||||
|
'show_contexts': True
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user