diff --git a/memory-bank/documentation/APIs.md b/memory-bank/documentation/APIs.md new file mode 100644 index 00000000..c55b8b72 --- /dev/null +++ b/memory-bank/documentation/APIs.md @@ -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 + +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 + +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 + +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 + +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 +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 + +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 +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 +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), +] \ No newline at end of file diff --git a/memory-bank/documentation/Architecture.md b/memory-bank/documentation/Architecture.md new file mode 100644 index 00000000..f83c3508 --- /dev/null +++ b/memory-bank/documentation/Architecture.md @@ -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) \ No newline at end of file diff --git a/memory-bank/documentation/Code.md b/memory-bank/documentation/Code.md new file mode 100644 index 00000000..5980b66d --- /dev/null +++ b/memory-bank/documentation/Code.md @@ -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 \ No newline at end of file diff --git a/memory-bank/documentation/Data.md b/memory-bank/documentation/Data.md new file mode 100644 index 00000000..78eaae36 --- /dev/null +++ b/memory-bank/documentation/Data.md @@ -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', + }, + }, +} \ No newline at end of file diff --git a/memory-bank/documentation/Features.md b/memory-bank/documentation/Features.md new file mode 100644 index 00000000..5a9a7951 --- /dev/null +++ b/memory-bank/documentation/Features.md @@ -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 \ No newline at end of file diff --git a/memory-bank/documentation/Issues.md b/memory-bank/documentation/Issues.md new file mode 100644 index 00000000..9f583807 --- /dev/null +++ b/memory-bank/documentation/Issues.md @@ -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 \ No newline at end of file diff --git a/memory-bank/documentation/Performance.md b/memory-bank/documentation/Performance.md new file mode 100644 index 00000000..b64c8d11 --- /dev/null +++ b/memory-bank/documentation/Performance.md @@ -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' +) \ No newline at end of file diff --git a/memory-bank/documentation/Security.md b/memory-bank/documentation/Security.md new file mode 100644 index 00000000..48d07358 --- /dev/null +++ b/memory-bank/documentation/Security.md @@ -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 \ No newline at end of file diff --git a/memory-bank/documentation/Testing.md b/memory-bank/documentation/Testing.md new file mode 100644 index 00000000..d94d9613 --- /dev/null +++ b/memory-bank/documentation/Testing.md @@ -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 +} \ No newline at end of file