mirror of
https://github.com/pacnpal/thrillwiki_django_no_react.git
synced 2025-12-20 15:11: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