Add comprehensive system architecture and feature documentation for ThrillWiki

This commit is contained in:
pacnpal
2025-02-18 10:08:46 -05:00
parent c19aaf2f4b
commit 0b51ee123a
9 changed files with 2828 additions and 0 deletions

View 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),
]

View 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)

View 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

View 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',
},
},
}

View 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

View 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

View 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'
)

View 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

View 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
}