PiqueTickets Producer Portal - Knowledge Base Feature Planning Document¶
Document Version: 1.0 Created: 2025-10-10 Status: Draft - Ready for Review Project: PIQUE-506 Producer Portal Knowledge Base
1. Executive Summary & Feature Overview¶
1.1 Feature Description¶
- Feature Name: Producer Portal Knowledge Base System
- Feature Type: New Feature
- Priority Level: High
- Estimated Timeline: 4-6 weeks
- Branch:
pique-506-producer-portal-knoweldge-base
1.2 Problem Statement¶
Current State: - No centralized documentation or help system for producers - Support requests handled manually via email/Slack - No self-service resources for common questions - Difficult to onboard new producers efficiently
Pain Points: - Producers lack easy access to platform documentation - Support team receives repetitive questions - No searchable knowledge repository - Missing visual guides and tutorials with images
User Impact: - Primary Users: Event producers using the producer portal - Secondary Users: PiqueTickets support team and administrators - Expected Impact: Reduced support burden, faster producer onboarding, improved self-service capabilities
Business Value: - Reduce support ticket volume by 40-60% - Improve producer satisfaction and retention - Scale support capabilities without additional headcount - Professional knowledge management aligned with industry standards
1.3 Expected Outcomes¶
Success Metrics: - 80% of producers access knowledge base within first month - 50% reduction in basic support inquiries - Average article helpfulness rating > 4.0/5.0 - Search functionality < 500ms response time - Page load time < 2 seconds
User Experience Goals: - Intuitive navigation with visual hierarchy - Fast full-text search with auto-suggestions - Mobile-responsive design matching producer portal branding - Rich media support (images, videos, embedded content) - Accessibility compliance (WCAG 2.1 AA)
Technical Goals: - Scalable architecture supporting 1000+ articles - Image optimization and CDN-ready - Analytics for content effectiveness - Easy content management for non-technical staff
2. Stakeholder Analysis & Requirements¶
2.1 Affected Users & Systems¶
Primary Users: - Event Producers: Need self-service documentation and tutorials - New Producers: Require onboarding guides and getting started content - Active Producers: Seek advanced features and troubleshooting guides
Secondary Users: - Support Team: Content creators and editors - Administrators: Knowledge base managers with analytics access - Superusers: Full editorial control and publishing rights
System Components:
- Django API Backend: New knowledge app
- Producer Portal Frontend: Knowledge base UI integration
- PostgreSQL Database: Article storage and full-text search
- S3/CloudFront: Image and media storage
Integration Points: - Authentication: Django JWT + NextAuth.js session management - User Management: Django User model + UserProfile - Media Storage: AWS S3 (existing configuration) - Email: Django email system for notifications - Search: PostgreSQL full-text search or django-watson
2.2 Functional Requirements¶
Must-Have Features:
- Article Management
- Rich text editor (CKEditor5 - already installed)
- Draft/Published/Archived workflow
- Featured image upload with optimization
- Automatic slug generation from title
- Category and tag assignment
- Estimated read time calculation
-
View count tracking
-
Category System
- Hierarchical categories (parent/child support)
- Icon support for visual identification
- Custom ordering
- Active/inactive status
-
Category-based navigation
-
Search Functionality
- Full-text search across title, content, tags
- Search result highlighting
- Filter by category, tags, date range
- Auto-suggest during typing
-
Recent searches tracking
-
User Feedback
- "Was this helpful?" voting
- Optional comment collection
- Helpful/not helpful counts per article
-
Feedback analytics in admin
-
Access Control
- Producer-only access (authenticated users)
- JWT authentication integration
- Permission-based article publishing
- Admin-only management interface
2.3 Non-Functional Requirements¶
Performance: - Page load time < 2 seconds (first contentful paint) - Search response time < 500ms - Support 100 concurrent users - Image optimization reducing file sizes by 40-60% - Lazy loading for below-the-fold images
Security: - JWT authentication for all endpoints - CSRF protection on forms - Image upload validation (type, size, dimensions) - XSS prevention on user-generated content - Rate limiting on search API (10 requests/minute) - Secure media file access control
Accessibility: - WCAG 2.1 AA compliance - Keyboard navigation support - Screen reader compatibility - Alt text required for all images - Proper heading hierarchy - Sufficient color contrast ratios
Browser/Platform Support: - Modern browsers (Chrome, Firefox, Safari, Edge - last 2 versions) - Mobile responsive (iOS Safari, Chrome Android) - Tablet optimization - Progressive enhancement approach
Reliability: - 99.9% uptime for knowledge base - Graceful degradation if search unavailable - Error handling with user-friendly messages - Database backup and recovery procedures - Monitoring and alerting for critical issues
3. Current State Analysis¶
3.1 Codebase Research Methodology¶
Tools Used: - File system exploration (mcp__serena tools) - Symbol-based code analysis (find_symbol, get_symbols_overview) - Pattern searching for existing implementations - Architecture documentation review (@CLAUDE-architecture.md) - Django settings and configuration analysis
3.2 Existing Architecture & Patterns¶
Tech Stack (Backend): - Framework: Django 5.1.13 - Database: PostgreSQL with connection pooling - ORM: Django ORM with optimized querysets - Storage: AWS S3 via django-storages 1.14.4 + boto3 1.36.20 - Search: PostgreSQL full-text search capabilities - Rich Text: django-ckeditor-5 0.2.17 (already installed) - Image Processing: Pillow 11.0.0 - REST API: Django REST Framework 3.15.2 - Authentication: djangorestframework-simplejwt 5.5.1
Tech Stack (Frontend - Producer Portal): - Framework: Next.js 15.2.4 with App Router - UI Library: Shadcn UI components - Styling: Tailwind CSS 4.0.0 - State: Zustand for global state - Forms: React Hook Form + Zod validation - Auth: NextAuth.js with JWT strategy
Architecture Pattern: - Service-Oriented Architecture: Business logic in service classes - RESTful API: ViewSets for CRUD, custom actions for complex operations - Layered Design: Models → Services → Serializers → Views - Template System: Django templates with Tailwind CSS - Admin Customization: Django Unfold 0.39.0 theme
Design Patterns in Use: - Slug Generation: Automatic unique slug creation (tickets/producers apps) - Image Processing: WebP conversion pipeline (Show model) - Soft Deletes: is_deleted + deleted_at pattern (Show model) - Draft Mode: Separate validation for draft vs published (Show model) - Service Layer: Transaction-decorated methods with tuple returns (ProducerService) - Signal Handlers: Auto-create related models (UserProfile creation)
Code Organization:
apps/api/
├── brktickets/ # Project settings and configuration
├── tickets/ # Core ticketing app (reference implementation)
├── producers/ # Producer management (reference for auth patterns)
├── portal/ # Producer portal backend endpoints
├── venue/ # Venue management (reference for slug/image patterns)
├── performer/ # Performer profiles (reference for rich text)
├── auth/ # Authentication views and JWT
├── email_tracking/ # Email delivery monitoring
├── theme/ # Tailwind theme configuration
└── templates/ # Shared Django templates
3.3 Relevant Existing Code¶
Similar Features for Reference:
- Show Model (
apps/api/tickets/models.py): - Slug generation: Automatic unique slugs from title
- Image processing: WebP conversion with dimension validation
- Draft workflow: Draft vs published with completion tracking
- Soft deletes: is_deleted pattern
-
Relevance: Direct pattern match for Article model
-
Producer Model (
apps/api/producers/models.py): - User association: UserProducerAssociation pattern
- Profile management: Extended user profiles with permissions
- Social media links: Multiple URL fields pattern
- Image uploads: Profile image handling
-
Relevance: Author relationship and permission patterns
-
Venue Model (
apps/api/venue/models.py): - Rich text content: CKEditor5 integration for descriptions
- Image management: Venue images with upload paths
-
Relevance: Content management patterns
-
Portal Views (
apps/api/portal/views.py): - Producer-specific filtering: Filter by authenticated user's producer
- Analytics aggregation: Dashboard metrics calculations
- Pagination patterns: DRF pagination setup
-
Relevance: Producer portal backend patterns
-
Authentication System (
apps/api/auth/): - JWT tokens: SimpleJWT configuration (48-hour access, 30-day refresh)
- Password reset: Token-based password recovery
- User permissions: Custom permission classes
- Relevance: Authentication and authorization patterns
Reusable Components:
-
Slug Generation Utility (
apps/api/api/utils.py): -
Image Processing (Show model pattern):
-
CKEditor5 Configuration (
apps/api/brktickets/settings.py): - Already configured with toolbar options
- Image upload support
-
Multiple configuration profiles
-
S3 Storage Configuration (
apps/api/brktickets/settings.py): - STORAGES configuration for media and static files
- CloudFront CDN setup ready
-
Public/private ACL patterns
-
Serializer Patterns (DRF):
- Nested serializers for related data
- Custom validation methods
- SerializerMethodField for computed values
3.4 Current Dependencies¶
Core Dependencies (Already Installed): - ✅ Django 5.1.13 - ✅ djangorestframework 3.15.2 - ✅ Pillow 11.0.0 (image processing) - ✅ django-ckeditor-5 0.2.17 (rich text editor) - ✅ psycopg2-binary 2.9.10 (PostgreSQL) - ✅ boto3 1.36.20 (AWS S3) - ✅ django-storages 1.14.4 - ✅ djangorestframework-simplejwt 5.5.1 - ✅ django-unfold 0.39.0 (admin theme) - ✅ celery 5.4.0 (async tasks) - ✅ django-filter 24.3 (API filtering)
New Dependencies Required: - 📦 python-slugify 8.0.0 - Enhanced slug generation - 📦 markdown 3.4.0 - Markdown rendering support (optional) - 📦 django-watson 1.6.0 - Advanced search capabilities (optional alternative to PostgreSQL full-text)
Development Dependencies: - ✅ coverage 7.6.12 (test coverage) - ✅ black 25.1.0 (code formatting) - ✅ mock 5.1.0 (testing)
Version Compatibility: - Python 3.12+ (current project standard) - Django 5.1+ compatible - PostgreSQL 15+ (current database)
3.5 Potential Conflicts & Constraints¶
Technical Debt: - None identified that would block knowledge base implementation - Existing apps follow consistent patterns suitable for extension
Legacy Code: - No legacy code in knowledge domain (greenfield implementation) - Existing patterns are modern and well-maintained
Resource Constraints: - Database: PostgreSQL connection pooling configured (600s max age, health checks) - Storage: S3 bucket already configured with CloudFront CDN option - Memory: Image optimization will reduce memory footprint - Performance: Existing indexes strategy can be applied to knowledge tables
Compliance Requirements: - GDPR: User feedback must allow deletion (implement cascade deletes) - Accessibility: WCAG 2.1 AA compliance required (producer portal standard) - Security: Follow existing CSRF, XSS, SQL injection prevention patterns - Data Retention: Follow existing soft-delete patterns for article archival
Integration Constraints: - Must integrate with existing JWT authentication (SimpleJWT) - Must work with NextAuth.js session management in producer portal - Must use existing S3/CloudFront media configuration - Must follow Unfold admin theme patterns for consistency - Cannot modify core Django User model (use existing UserProfile pattern)
Naming Conventions:
- App name: knowledge (lowercase, no underscores per Django convention)
- URL prefix: /api/v1/portal/knowledge/ (nest under portal)
- Model names: PascalCase (Category, Article, Tag, ArticleFeedback)
- Serializer names: {Model}Serializer
- ViewSet names: {Model}ViewSet
4. Research & Best Practices¶
4.1 Industry Standards Research¶
Research Sources: - Django documentation (official patterns) - Django REST Framework best practices - Knowledge base UX research (Intercom, Zendesk, GitBook patterns) - PostgreSQL full-text search documentation - Accessibility guidelines (WCAG 2.1)
Industry Best Practices:
- Content Organization:
- Hierarchical category structure (max 2-3 levels deep)
- Tag-based cross-referencing for discoverability
- Featured articles for common topics
- Breadcrumb navigation for context
-
Related articles based on category/tags
-
Search Experience:
- Search-as-you-type with instant results
- Filters for refinement (category, date, popularity)
- Search result highlighting
- "Did you mean?" suggestions for typos
-
Recent/popular searches display
-
Article Quality:
- Estimated read time (avg 200-250 words/minute)
- Table of contents for long articles (>500 words)
- Clear headings and subheadings hierarchy
- Visual elements every 200-300 words
-
Actionable content with step-by-step guides
-
Feedback Mechanisms:
- Binary helpful/not helpful voting
- Optional detailed feedback collection
- Feedback analytics for content improvement
-
Display aggregate ratings to users
-
Performance:
- Lazy loading for images below fold
- CDN for static assets and images
- Database query optimization (select_related, prefetch_related)
- Pagination for long lists
4.2 Framework-Specific Patterns¶
Django Best Practices:
-
Model Design:
# Slug generation on save def save(self, *args, **kwargs): if not self.slug: self.slug = generate_unique_slug(self, self.title) super().save(*args, **kwargs) # Calculated fields as properties @property def estimated_read_time(self): words = len(self.content.split()) return max(1, round(words / 200)) -
QuerySet Optimization:
-
PostgreSQL Full-Text Search:
-
Admin Customization (Unfold):
@admin.register(Article) class ArticleAdmin(admin.ModelAdmin): list_display = ['title', 'category', 'status', 'view_count', 'helpful_count'] list_filter = ['status', 'category', 'featured', 'created_at'] search_fields = ['title', 'content'] prepopulated_fields = {'slug': ('title',)} actions = ['publish_articles', 'archive_articles']
Django REST Framework Patterns:
-
ViewSet Design:
class ArticleViewSet(viewsets.ModelViewSet): serializer_class = ArticleSerializer permission_classes = [IsAuthenticated] filter_backends = [DjangoFilterBackend, filters.SearchFilter] filterset_fields = ['category', 'tags', 'status'] search_fields = ['title', 'content'] def get_queryset(self): return Article.objects.filter( status='published' ).select_related('category', 'author').prefetch_related('tags') @action(detail=True, methods=['post']) def mark_helpful(self, request, pk=None): # Custom action for feedback pass -
Serializer Patterns:
class ArticleSerializer(serializers.ModelSerializer): category_name = serializers.CharField(source='category.name', read_only=True) tags = TagSerializer(many=True, read_only=True) estimated_read_time = serializers.IntegerField(read_only=True) class Meta: model = Article fields = ['id', 'title', 'slug', 'content', 'category', 'category_name', 'tags', 'estimated_read_time'] read_only_fields = ['slug', 'view_count', 'created_at']
4.3 Security Guidelines¶
OWASP Best Practices:
- Input Validation:
- Validate image file types (JPEG, PNG, WebP only)
- Maximum file size limits (5MB for images)
- Sanitize HTML content from rich text editor
-
Validate slug format (alphanumeric + hyphens only)
-
Output Encoding:
- Django template auto-escaping (already enabled)
- DRF JSON encoding (automatic)
-
Image URLs with signed URLs for private content
-
Authentication & Authorization:
- JWT token validation on all endpoints
- Producer-only access control
- Permission-based publishing rights
-
CSRF tokens on admin forms
-
Rate Limiting:
-
SQL Injection Prevention:
- Use Django ORM (parameterized queries)
- Never use raw SQL with user input
- Use Django's Q objects for complex queries
Image Upload Security:
from PIL import Image
from django.core.exceptions import ValidationError
def validate_image(file):
# Validate file size
if file.size > 5 * 1024 * 1024: # 5MB
raise ValidationError("Image size cannot exceed 5MB")
# Validate image type
try:
img = Image.open(file)
if img.format not in ['JPEG', 'PNG', 'WEBP']:
raise ValidationError("Only JPEG, PNG, and WebP images allowed")
except Exception:
raise ValidationError("Invalid image file")
4.4 Performance Benchmarks¶
Target Metrics: - Page load time: < 2 seconds (first contentful paint) - Search response: < 500ms - Image optimization: 40-60% size reduction - Database query count: < 10 per article page
Optimization Strategies:
-
Database Indexes:
-
Image Optimization:
- WebP conversion (85% quality)
- Multiple sizes (thumbnail: 150x150, medium: 600x400, large: 1200x800)
- Lazy loading with intersection observer
-
CDN delivery
-
Query Optimization:
# Efficient queryset with annotations Article.objects.filter(status='published').select_related( 'category', 'author' ).prefetch_related('tags').annotate( helpful_ratio=Case( When(helpful_count__gt=0, then=F('helpful_count') * 100 / (F('helpful_count') + F('not_helpful_count'))), default=0, output_field=FloatField() ) )
5. Solution Design¶
5.1 Proposed Architecture¶
High-Level System Design:
┌─────────────────────────────────────────────────────────────┐
│ Producer Portal Frontend │
│ (Next.js App Router) │
│ ┌──────────────┬──────────────┬─────────────────────────┐ │
│ │ Knowledge │ Article │ Search │ │
│ │ Home │ Detail │ Results │ │
│ └──────────────┴──────────────┴─────────────────────────┘ │
└────────────────────────┬────────────────────────────────────┘
│ HTTP/JSON (JWT Auth)
↓
┌─────────────────────────────────────────────────────────────┐
│ Django REST API Backend │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ knowledge.views (ViewSets & Custom Views) │ │
│ │ ├─ ArticleViewSet │ │
│ │ ├─ CategoryViewSet │ │
│ │ ├─ TagViewSet │ │
│ │ ├─ SearchView (custom) │ │
│ │ └─ FeedbackView (custom) │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ knowledge.serializers │ │
│ │ ├─ ArticleSerializer │ │
│ │ ├─ ArticleDetailSerializer (with related data) │ │
│ │ ├─ CategorySerializer │ │
│ │ └─ FeedbackSerializer │ │
│ └──────────────────────────────────────────────────────┘ │
│ ↓ │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ knowledge.models │ │
│ │ ├─ Category (hierarchical) │ │
│ │ ├─ Tag │ │
│ │ ├─ Article (main content) │ │
│ │ └─ ArticleFeedback │ │
│ └──────────────────────────────────────────────────────┘ │
└────────────────┬───────────────────────┬───────────────────┘
│ │
↓ ↓
┌────────────────────────┐ ┌──────────────────────┐
│ PostgreSQL Database │ │ AWS S3 Storage │
│ - Articles │ │ - Featured Images │
│ - Categories │ │ - Article Assets │
│ - Tags │ │ - Thumbnails │
│ - Feedback │ │ - WebP Optimized │
│ - Full-text Search │ └──────────────────────┘
└────────────────────────┘
Data Model Design:
┌────────────────────┐
│ Category │
│ │
│ - id (PK) │
│ - name │
│ - slug (unique) │
│ - description │
│ - icon │
│ - order │
│ - parent_id (FK) │──┐ Self-referential
│ - is_active │ │ for hierarchy
│ - created_at │ │
│ - updated_at │←─┘
└─────────┬──────────┘
│
│ Many-to-One
│
↓
┌────────────────────┐ ┌────────────────────┐
│ Article │ │ Tag │
│ │ │ │
│ - id (PK) │ │ - id (PK) │
│ - title │ │ - name (unique) │
│ - slug (unique) │ │ - slug (unique) │
│ - content (text) │ │ - created_at │
│ - excerpt │ └────────────────────┘
│ - category_id(FK) │ ↑
│ - author_id (FK) │ │
│ - status │ │ Many-to-Many
│ - featured │ │
│ - featured_image │ ┌────────┴───────────┐
│ - image_alt_text │ │ Article_Tags │
│ - view_count │ │ │
│ - helpful_count │ │ - article_id (FK) │
│ - not_helpful_ct │ │ - tag_id (FK) │
│ - published_at │ └────────────────────┘
│ - created_at │
│ - updated_at │
│ - meta_desc │
│ - est_read_time │
└─────────┬──────────┘
│
│ One-to-Many
│
↓
┌────────────────────┐ ┌────────────────────┐
│ ArticleFeedback │ │ User │
│ │ │ (Django Auth) │
│ - id (PK) │ │ │
│ - article_id (FK) │ │ - id (PK) │
│ - user_id (FK) │───────→│ - username │
│ - is_helpful │ │ - email │
│ - comment │ │ - ... │
│ - created_at │ └────────────────────┘
└────────────────────┘
API Endpoint Design:
Base URL: /api/v1/portal/knowledge/
Endpoints:
├── /categories/
│ ├── GET / # List all active categories
│ ├── GET /{slug}/ # Get category by slug
│ └── GET /{slug}/articles/ # List articles in category
│
├── /tags/
│ ├── GET / # List all tags
│ └── GET /{slug}/articles/ # List articles with tag
│
├── /articles/
│ ├── GET / # List articles (paginated, filterable)
│ │ ?category={slug}
│ │ ?tag={slug}
│ │ ?status=published
│ │ ?featured=true
│ │ ?search={query}
│ │ ?page={num}
│ │ ?page_size={num}
│ │
│ ├── GET /{slug}/ # Get article detail
│ ├── GET /featured/ # Get featured articles
│ ├── GET /recent/ # Get recent articles
│ ├── GET /popular/ # Get most viewed articles
│ │
│ └── POST /{slug}/feedback/ # Submit article feedback
│ {
│ "is_helpful": true,
│ "comment": "optional"
│ }
│
└── /search/
├── GET / # Full-text search
│ ?q={query}
│ ?category={slug}
│ ?page={num}
│
└── GET /suggestions/ # Search autocomplete
?q={partial_query}
URL Structure (Frontend):
Producer Portal Routes:
├── /dashboard/knowledge/
│ ├── / # Knowledge base home
│ ├── /category/{slug}/ # Category view
│ ├── /article/{slug}/ # Article detail
│ ├── /tag/{slug}/ # Tag view
│ └── /search?q={query} # Search results
5.2 Technology Decisions¶
Backend Technology Stack:
| Component | Technology | Justification |
|---|---|---|
| Rich Text Editor | CKEditor5 (existing) | Already configured, supports images, proven in Performer/Venue apps |
| Image Processing | Pillow (existing) | Handles WebP conversion, already used in Show model |
| Search Engine | PostgreSQL Full-Text Search | Native to PostgreSQL, no additional infrastructure, sufficient for <10K articles |
| Storage | AWS S3 + CloudFront (existing) | Already configured, CDN-ready, cost-effective |
| Slug Generation | Django slugify + uniqueness check | Proven pattern in Show/Producer models |
| API Framework | Django REST Framework (existing) | Consistent with existing portal endpoints |
New Dependencies:
- python-slugify 8.0.0
- Purpose: Enhanced slug generation with Unicode support
- Why: Better than Django's default slugify for international characters
- Alternatives Considered: Django's slugify (less robust), manual implementation (more work)
-
Risk: Minimal, stable library with 10M+ downloads
-
markdown 3.4.0 (Optional)
- Purpose: Markdown rendering if we want to support Markdown in addition to HTML
- Why: Some users prefer Markdown for technical documentation
- Alternatives Considered: Rich text only (simpler but less flexible)
-
Risk: Low, optional feature that can be added later
-
django-watson 1.6.0 (Alternative)
- Purpose: Advanced full-text search with better relevance ranking
- Why: Better than PostgreSQL search for large scale, easier to use
- Alternatives Considered: PostgreSQL full-text (simpler, no dependencies), Elasticsearch (overkill)
- Risk: Low, can start with PostgreSQL and migrate if needed
- Decision: Start with PostgreSQL, add django-watson if search quality insufficient
Frontend Technology Stack:
| Component | Technology | Justification |
|---|---|---|
| Framework | Next.js 15 App Router (existing) | Already used in producer portal |
| UI Components | Shadcn UI (existing) | Consistent with portal design system |
| Styling | Tailwind CSS (existing) | Matches existing producer portal |
| Forms | React Hook Form + Zod (existing) | Standard for producer portal |
| Data Fetching | Native fetch with SWR pattern | Consistent with portal patterns |
| Rich Text Display | DOMPurify + dangerouslySetInnerHTML | XSS protection for HTML content |
Alternative Solutions Considered:
- Headless CMS (Contentful, Strapi)
- Pros: Feature-rich, managed service, non-technical content editing
- Cons: Additional cost, vendor lock-in, learning curve, over-engineered
-
Decision: Rejected - Django admin sufficient for V1
-
Wiki Software (MediaWiki, DokuWiki)
- Pros: Purpose-built for documentation, versioning, mature
- Cons: Separate authentication, difficult integration, different tech stack
-
Decision: Rejected - Maintain tech stack consistency
-
Static Site Generator (Docusaurus, GitBook)
- Pros: Fast, version-controlled, markdown-based
- Cons: Not dynamic, no user feedback, deployment complexity
- Decision: Rejected - Need dynamic features (search, feedback, analytics)
5.3 Security Considerations¶
Threat Model:
| Threat | Likelihood | Impact | Mitigation |
|---|---|---|---|
| Unauthorized access to articles | Medium | Medium | JWT authentication on all endpoints |
| XSS via article content | High | High | CKEditor5 sanitization + DOMPurify on frontend |
| SQL injection via search | Low | Critical | PostgreSQL parameterized queries via ORM |
| CSRF on feedback forms | Medium | Medium | Django CSRF tokens (already enabled) |
| Image upload malware | Low | High | File type validation, size limits, virus scanning (future) |
| DoS via search | Medium | Medium | Rate limiting (10 req/min per user) |
| Data breach of user feedback | Low | Medium | Encrypted database, secure backups |
Authentication & Authorization:
# JWT Authentication (already configured)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
),
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
}
# Custom permission for knowledge base
class IsProducerUser(permissions.BasePermission):
"""
Only allow access to authenticated producers.
"""
def has_permission(self, request, view):
return (
request.user and
request.user.is_authenticated and
hasattr(request.user, 'producer_profile')
)
Input Validation:
# Article model validation
from django.core.validators import FileExtensionValidator, MaxValueValidator
class Article(models.Model):
# Image validation
featured_image = models.ImageField(
upload_to='knowledge/articles/',
blank=True,
validators=[
FileExtensionValidator(['jpg', 'jpeg', 'png', 'webp']),
validate_image_size, # Custom validator for 5MB limit
]
)
# Content validation
content = models.TextField(
validators=[MaxLengthValidator(50000)] # Prevent abuse
)
def clean(self):
# Additional validation logic
if self.status == 'published':
if not self.title or not self.content or not self.category:
raise ValidationError("Published articles must have title, content, and category")
Output Encoding:
# Serializer with safe HTML rendering
class ArticleSerializer(serializers.ModelSerializer):
# Content is already sanitized by CKEditor5
# Frontend will use DOMPurify as additional layer
def to_representation(self, instance):
data = super().to_representation(instance)
# Ensure all URLs are absolute for images
if data.get('featured_image'):
data['featured_image'] = request.build_absolute_uri(data['featured_image'])
return data
Rate Limiting:
# settings.py
RATELIMIT_ENABLE = True
# views.py
from django_ratelimit.decorators import ratelimit
from django_ratelimit.exceptions import Ratelimited
class SearchView(APIView):
@ratelimit(key='user', rate='10/m', method='GET')
def get(self, request):
# Search implementation
pass
Data Protection:
- Encryption at Rest: PostgreSQL encryption (AWS RDS default)
- Encryption in Transit: HTTPS only (SECURE_SSL_REDIRECT=True in production)
- Backup Strategy: Automated daily backups with 30-day retention
- Access Logs: Track all article views and feedback submissions
- GDPR Compliance: Allow users to delete their feedback (CASCADE deletes)
6. Implementation Plan¶
6.1 Development Phases¶
Phase 1: Foundation & Models [Timeline: Week 1]
Objective: Set up Django app structure, create database models, configure admin
Tasks:
- [x] Create knowledge Django app
- Files: apps/api/knowledge/__init__.py, apps/api/knowledge/apps.py
- Add to INSTALLED_APPS in apps/api/brktickets/settings.py:84
- Create database models
- File:
apps/api/knowledge/models.py - Models: Category, Tag, Article, ArticleFeedback
-
Include Meta classes with indexes and ordering
-
Create model admin classes
- File:
apps/api/knowledge/admin.py - Configure Unfold admin for all models
- Add list_display, list_filter, search_fields
- Configure CKEditor5 for Article.content
-
Add custom actions (publish, archive, bulk operations)
-
Create and run migrations
- Command:
python manage.py makemigrations knowledge - Command:
python manage.py migrate -
Verify tables created in PostgreSQL
-
Create model fixtures for testing
- File:
apps/api/knowledge/fixtures/sample_data.json -
5-10 categories, 20-30 tags, 50+ sample articles
-
Write unit tests for models
- File:
apps/api/knowledge/tests/test_models.py - Test slug generation, read time calculation
- Test model validation and relationships
- Test soft delete behavior
Deliverable: - Fully functional Django app with database models - Admin interface for content management - Passing unit tests (>90% model coverage)
Phase 2: API & Serializers [Timeline: Week 2]
Objective: Build REST API endpoints with DRF
Tasks:
- [ ] Create serializers
- File: apps/api/knowledge/serializers.py
- CategorySerializer (with article count)
- TagSerializer
- ArticleListSerializer (summary view)
- ArticleDetailSerializer (with related data)
- FeedbackSerializer
- Implement nested serializers for related objects
- Create ViewSets
- File:
apps/api/knowledge/views.py - CategoryViewSet (read-only for producers)
- TagViewSet (read-only)
- ArticleViewSet (with custom actions)
- Custom action: increment_view_count
- Custom action: get_related_articles
- Custom action: submit_feedback
-
Implement queryset optimization (select_related, prefetch_related)
-
Implement search functionality
- File:
apps/api/knowledge/views.py(SearchView class) - PostgreSQL full-text search implementation
- Search ranking by relevance
- Filter by category, tags, date range
-
Pagination support
-
Implement filtering
- File:
apps/api/knowledge/filters.py - ArticleFilter with django-filter
- Category, tag, status, featured filters
-
Date range filters
-
Configure URL routing
- File:
apps/api/knowledge/urls.py - Register ViewSets with DRF router
- Add custom search endpoint
- Nest under
/api/v1/portal/knowledge/ -
Update:
apps/api/portal/urls.pyto include knowledge URLs -
Write API integration tests
- File:
apps/api/knowledge/tests/test_api.py - Test all endpoints with authentication
- Test filtering, searching, pagination
- Test feedback submission
- Test permissions (producer-only access)
Deliverable: - Fully functional REST API - Comprehensive test coverage (>85%) - API documentation via DRF browsable API
Phase 3: Frontend Integration [Timeline: Week 3]
Objective: Build Next.js frontend for knowledge base
Tasks:
- [ ] Create API client functions
- File: apps/producer/src/lib/api/knowledge.ts
- fetchCategories()
- fetchArticles(filters)
- fetchArticleBySlug(slug)
- searchArticles(query)
- submitFeedback(articleId, feedback)
- Create TypeScript types
- File:
apps/producer/src/types/knowledge.ts - Interface: Category, Tag, Article, ArticleFeedback
-
Type: SearchFilters, ArticleStatus
-
Build page components
-
File:
apps/producer/src/app/dashboard/knowledge/page.tsx- Knowledge base home with category grid
- Featured articles carousel
- Recent articles list
- Quick stats display
-
File:
apps/producer/src/app/dashboard/knowledge/category/[slug]/page.tsx- Category article list with filters
- Subcategory navigation
- Sort controls (newest, most viewed, most helpful)
-
File:
apps/producer/src/app/dashboard/knowledge/article/[slug]/page.tsx- Article detail view with hero image
- Breadcrumb navigation
- Table of contents (auto-generated)
- Related articles sidebar
- Feedback widget
- Social sharing buttons
-
File:
apps/producer/src/app/dashboard/knowledge/search/page.tsx- Search results page
- Filter sidebar
- Result highlighting
- Pagination controls
-
Build UI components
- File:
apps/producer/src/components/knowledge/ArticleCard.tsx - File:
apps/producer/src/components/knowledge/CategoryCard.tsx - File:
apps/producer/src/components/knowledge/FeedbackWidget.tsx - File:
apps/producer/src/components/knowledge/SearchBar.tsx - File:
apps/producer/src/components/knowledge/TableOfContents.tsx -
Use Shadcn UI components for consistency
-
Implement search with debouncing
- File:
apps/producer/src/hooks/useSearch.ts - Custom hook with debounce (300ms)
- Auto-suggest functionality
-
Search history tracking
-
Add to navigation menu
- File:
apps/producer/src/components/layout/Sidebar.tsx - Add "Knowledge Base" link with icon
- Position under Dashboard, above Settings
Deliverable: - Fully functional frontend interface - Mobile-responsive design - Integrated with producer portal navigation
Phase 4: Image Processing & Optimization [Timeline: Week 4]
Objective: Implement image upload, optimization, and CDN delivery
Tasks:
- [ ] Create image processing utilities
- File: apps/api/knowledge/utils/image_processing.py
- Function: convert_to_webp(image_file)
- Function: generate_thumbnails(image_file, sizes)
- Function: validate_image_dimensions(image, min_width, min_height)
- Function: optimize_image_quality(image, quality=85)
- Add image processing to Article model
- Update:
apps/api/knowledge/models.py - Override save() method to process featured_image
- Generate multiple sizes on upload
-
Store WebP versions with fallbacks
-
Configure image upload in admin
- Update:
apps/api/knowledge/admin.py - Add image preview in list view
- Add image cropping interface (optional)
-
Show image dimensions and file size
-
Implement lazy loading on frontend
- Update all image components to use lazy loading
- Use Intersection Observer API
-
Add skeleton loaders for images
-
Configure CDN headers
- Update: S3 bucket configuration
-
Configure CloudFront distribution (if needed)
-
Create management command for image optimization
- File:
apps/api/knowledge/management/commands/optimize_images.py - Regenerate all thumbnails
- Convert existing images to WebP
- Clean orphaned media files
Deliverable: - Optimized image pipeline - 40-60% file size reduction - Fast image loading with lazy loading
Phase 5: Search Enhancement & Analytics [Timeline: Week 5]
Objective: Advanced search features and analytics dashboard
Tasks:
- [ ] Enhance search with ranking
- Update: apps/api/knowledge/views.py (SearchView)
- Implement relevance scoring
- Add search term highlighting
- Add "Did you mean?" suggestions (optional)
- Implement search autocomplete
- File:
apps/api/knowledge/views.py(SearchSuggestionsView) - Endpoint:
/api/v1/portal/knowledge/search/suggestions/ -
Return top 5 suggestions based on partial query
-
Create analytics models
- File:
apps/api/knowledge/models.py - Model: SearchQuery (track popular searches)
- Model: ArticleView (detailed view tracking)
-
Add analytics aggregation methods
-
Build admin analytics dashboard
- File:
apps/api/knowledge/admin.py - Custom admin view for analytics
- Most viewed articles chart
- Most helpful articles chart
- Popular search terms
-
View trends over time
-
Add performance monitoring
- Install django-debug-toolbar (dev only)
- Monitor query counts per page
- Optimize slow queries
- Add database indexes as needed
Deliverable: - Enhanced search experience - Analytics dashboard for content team - Optimized performance
Phase 6: Testing, Polish & Deployment [Timeline: Week 6]
Objective: Comprehensive testing, bug fixes, and production deployment
Tasks:
- [ ] Write comprehensive test suite
- File: apps/api/knowledge/tests/test_views.py
- File: apps/api/knowledge/tests/test_serializers.py
- File: apps/api/knowledge/tests/test_search.py
- File: apps/api/knowledge/tests/test_permissions.py
- Achieve >85% code coverage
- Perform accessibility audit
- Test with screen readers (NVDA, JAWS)
- Verify keyboard navigation
- Check color contrast ratios
- Validate ARIA labels
-
Run automated accessibility tests (axe)
-
Performance testing
- Load test with 100 concurrent users
- Verify page load times < 2s
- Check search response times < 500ms
- Monitor database query counts
-
Test with large datasets (1000+ articles)
-
Security audit
- Test authentication/authorization
- Verify XSS protection
- Test CSRF protection
- Validate rate limiting
- Check image upload security
-
SQL injection testing
-
Create documentation
- File:
apps/api/knowledge/README.md - API documentation
- Content management guide
- Search optimization tips
-
Troubleshooting guide
-
Create sample content
- Write 20-30 initial articles
- Cover common producer questions
- Include getting started guides
- Add troubleshooting articles
-
Upload optimized images
-
Database migration for production
- Create migration plan
- Test migrations on staging
- Plan rollback strategy
-
Schedule maintenance window
-
Deploy to staging
- Deploy Django app changes
- Deploy frontend changes
- Run smoke tests
- Verify S3 integration
-
Test search functionality
-
User acceptance testing
- Internal testing with team
- Beta testing with select producers
- Collect feedback
-
Fix critical bugs
-
Deploy to production
- Execute deployment plan
- Monitor error logs
- Monitor performance metrics
- Verify CDN delivery
- Announce to users
Deliverable: - Production-ready knowledge base - Comprehensive documentation - Passing all tests and audits - Successfully deployed to production
6.2 Detailed Task Breakdown¶
| Task | Files Affected | Estimated Hours | Dependencies | Priority |
|---|---|---|---|---|
| Phase 1: Foundation | ||||
| Create Django app structure | apps/api/knowledge/__init__.py, apps.py, settings.py |
1 | None | Critical |
| Design and implement Category model | apps/api/knowledge/models.py |
3 | App structure | Critical |
| Design and implement Tag model | apps/api/knowledge/models.py |
2 | App structure | Critical |
| Design and implement Article model | apps/api/knowledge/models.py |
6 | Category, Tag models | Critical |
| Design and implement ArticleFeedback model | apps/api/knowledge/models.py |
2 | Article model | Critical |
| Configure admin for Category | apps/api/knowledge/admin.py |
2 | Category model | High |
| Configure admin for Tag | apps/api/knowledge/admin.py |
1 | Tag model | High |
| Configure admin for Article | apps/api/knowledge/admin.py |
4 | Article model | High |
| Configure admin for ArticleFeedback | apps/api/knowledge/admin.py |
2 | Feedback model | High |
| Create database migrations | Migration files | 1 | All models | Critical |
| Create sample data fixtures | fixtures/sample_data.json |
4 | Migrations | Medium |
| Write model unit tests | tests/test_models.py |
8 | All models | High |
| Phase 2: API | ||||
| Create CategorySerializer | serializers.py |
2 | Category model | High |
| Create TagSerializer | serializers.py |
1 | Tag model | High |
| Create ArticleListSerializer | serializers.py |
3 | Article model | High |
| Create ArticleDetailSerializer | serializers.py |
4 | Article model | High |
| Create FeedbackSerializer | serializers.py |
2 | Feedback model | High |
| Implement CategoryViewSet | views.py |
3 | CategorySerializer | High |
| Implement TagViewSet | views.py |
2 | TagSerializer | High |
| Implement ArticleViewSet | views.py |
8 | ArticleSerializers | Critical |
| Implement SearchView | views.py |
6 | Article model | Critical |
| Configure article filters | filters.py |
3 | Article model | Medium |
| Configure URL routing | urls.py, portal/urls.py |
2 | All ViewSets | Critical |
| Write API integration tests | tests/test_api.py |
12 | All ViewSets | High |
| Phase 3: Frontend | ||||
| Create TypeScript types | src/types/knowledge.ts |
2 | API design | High |
| Create API client functions | src/lib/api/knowledge.ts |
4 | API endpoints | Critical |
| Build knowledge base home page | app/dashboard/knowledge/page.tsx |
8 | API client | Critical |
| Build category page | app/.../category/[slug]/page.tsx |
6 | API client | High |
| Build article detail page | app/.../article/[slug]/page.tsx |
10 | API client | Critical |
| Build search results page | app/.../search/page.tsx |
6 | Search API | High |
| Create ArticleCard component | components/knowledge/ArticleCard.tsx |
3 | Types | Medium |
| Create CategoryCard component | components/knowledge/CategoryCard.tsx |
3 | Types | Medium |
| Create FeedbackWidget component | components/knowledge/FeedbackWidget.tsx |
4 | Feedback API | Medium |
| Create SearchBar component | components/knowledge/SearchBar.tsx |
4 | Search API | Medium |
| Create TableOfContents component | components/knowledge/TableOfContents.tsx |
4 | None | Low |
| Implement search hook | hooks/useSearch.ts |
3 | Search API | Medium |
| Add navigation menu link | components/layout/Sidebar.tsx |
1 | None | Medium |
| Phase 4: Images | ||||
| Create image processing utilities | utils/image_processing.py |
6 | Pillow | High |
| Add image processing to Article save | models.py |
4 | Utilities | High |
| Configure image upload in admin | admin.py |
3 | Model updates | Medium |
| Implement lazy loading | Frontend components | 4 | None | Medium |
| Configure CDN headers | S3 settings | 2 | None | Medium |
| Create image optimization command | management/commands/optimize_images.py |
4 | Utilities | Low |
| Phase 5: Search & Analytics | ||||
| Enhance search ranking | views.py (SearchView) |
6 | Search implementation | Medium |
| Implement autocomplete | views.py (SearchSuggestionsView) |
4 | Search | Medium |
| Create analytics models | models.py |
4 | None | Low |
| Build analytics dashboard | admin.py |
8 | Analytics models | Low |
| Add performance monitoring | Multiple files | 4 | django-debug-toolbar | Low |
| Phase 6: Testing & Deployment | ||||
| Write view tests | tests/test_views.py |
10 | All views | High |
| Write serializer tests | tests/test_serializers.py |
6 | All serializers | High |
| Write search tests | tests/test_search.py |
6 | Search implementation | High |
| Write permission tests | tests/test_permissions.py |
4 | Permissions | High |
| Accessibility audit | All frontend files | 8 | Frontend complete | High |
| Performance testing | N/A | 6 | Complete app | High |
| Security audit | N/A | 6 | Complete app | Critical |
| Create documentation | README.md, docs |
8 | Complete app | Medium |
| Create sample content | Admin interface | 12 | Deployment ready | Medium |
| Deploy to staging | Infrastructure | 4 | Tests passing | High |
| User acceptance testing | N/A | 8 | Staging deployment | High |
| Production deployment | Infrastructure | 6 | UAT passing | Critical |
Total Estimated Hours: 280-320 hours (approximately 6-8 weeks with 1 developer)
6.3 File Change Summary¶
New Files:
Backend (apps/api/knowledge/):
apps/api/knowledge/
├── __init__.py
├── apps.py
├── models.py # Category, Tag, Article, ArticleFeedback
├── admin.py # Unfold admin configuration
├── serializers.py # DRF serializers
├── views.py # ViewSets and custom views
├── urls.py # URL routing
├── filters.py # Django-filter configuration
├── permissions.py # Custom permissions
├── utils/
│ ├── __init__.py
│ ├── image_processing.py # Image optimization utilities
│ └── search.py # Search utilities
├── management/
│ └── commands/
│ ├── __init__.py
│ ├── import_articles.py # CSV import command
│ ├── generate_sample_knowledge.py # Sample data generator
│ ├── update_read_times.py # Batch update read times
│ ├── regenerate_thumbnails.py # Batch image processing
│ └── clean_orphaned_media.py # Media cleanup
├── migrations/
│ ├── __init__.py
│ └── 0001_initial.py # Initial models migration
├── fixtures/
│ └── sample_data.json # Sample categories and articles
└── tests/
├── __init__.py
├── test_models.py # Model unit tests
├── test_serializers.py # Serializer tests
├── test_views.py # View/API tests
├── test_search.py # Search functionality tests
└── test_permissions.py # Permission tests
Frontend (apps/producer/src/):
apps/producer/src/
├── app/dashboard/knowledge/
│ ├── page.tsx # Knowledge base home
│ ├── layout.tsx # Knowledge base layout
│ ├── category/
│ │ └── [slug]/
│ │ └── page.tsx # Category article list
│ ├── article/
│ │ └── [slug]/
│ │ └── page.tsx # Article detail view
│ ├── tag/
│ │ └── [slug]/
│ │ └── page.tsx # Tag article list
│ └── search/
│ └── page.tsx # Search results
├── components/knowledge/
│ ├── ArticleCard.tsx # Article preview card
│ ├── CategoryCard.tsx # Category card with icon
│ ├── FeedbackWidget.tsx # Helpful/not helpful widget
│ ├── SearchBar.tsx # Search input with autocomplete
│ ├── TableOfContents.tsx # Auto-generated TOC
│ ├── RelatedArticles.tsx # Related articles sidebar
│ └── Breadcrumbs.tsx # Navigation breadcrumbs
├── lib/api/
│ └── knowledge.ts # API client functions
├── types/
│ └── knowledge.ts # TypeScript interfaces
├── hooks/
│ ├── useSearch.ts # Search with debounce
│ ├── useArticle.ts # Article fetching hook
│ └── useCategories.ts # Categories fetching hook
└── styles/knowledge/
├── article.css # Article-specific styles
└── knowledge.css # General knowledge base styles
Modified Files:
Backend:
- apps/api/brktickets/settings.py:84 - Add 'knowledge' to INSTALLED_APPS
- apps/api/portal/urls.py - Include knowledge URLs under portal
- apps/api/brktickets/settings.py - Add knowledge-specific settings (optional)
Frontend:
- apps/producer/src/components/layout/Sidebar.tsx - Add knowledge base nav link
- apps/producer/src/app/dashboard/layout.tsx - Update navigation (if needed)
Documentation:
- apps/api/knowledge/README.md - Knowledge base documentation (new)
- docs/knowledge-base-planning.md - This planning document (new)
- @CLAUDE-architecture.md - Update with knowledge base info
Deleted Files: - None (greenfield implementation)
7. Testing Strategy¶
7.1 Test Coverage Plan¶
Unit Tests (Backend):
-
Model Tests (
apps/api/knowledge/tests/test_models.py):class CategoryModelTest(TestCase): def test_slug_generation_from_name() def test_hierarchical_category_relationship() def test_category_ordering() def test_active_inactive_status() class ArticleModelTest(TestCase): def test_slug_uniqueness() def test_estimated_read_time_calculation() def test_status_transitions() def test_view_count_increment() def test_soft_delete_behavior() def test_featured_image_processing() def test_published_articles_only_query() class ArticleFeedbackTest(TestCase): def test_feedback_submission() def test_user_can_only_submit_once() def test_helpful_count_update() -
Serializer Tests (
tests/test_serializers.py): -
View Tests (
tests/test_views.py):class ArticleViewSetTest(APITestCase): def test_list_articles_requires_authentication() def test_filter_by_category() def test_filter_by_tag() def test_pagination() def test_view_count_increment() def test_related_articles() class SearchViewTest(APITestCase): def test_search_by_title() def test_search_by_content() def test_search_ranking() def test_search_filters() def test_rate_limiting() -
Permission Tests (
tests/test_permissions.py):
Integration Tests (Backend):
- API Flow Tests:
- Complete user journey from home to article to feedback
- Search → filter → view article → submit feedback
- Category navigation → subcategory → article
-
Image upload → processing → retrieval
-
Database Tests:
- Foreign key constraints
- Cascade deletes
- Index performance
- Transaction rollbacks
Frontend Tests:
-
Component Tests (Jest + React Testing Library):
// ArticleCard.test.tsx describe('ArticleCard', () => { it('renders article title and excerpt') it('displays featured image with lazy loading') it('shows estimated read time') it('navigates to article on click') }); // FeedbackWidget.test.tsx describe('FeedbackWidget', () => { it('renders helpful/not helpful buttons') it('submits feedback on button click') it('shows thank you message after submission') it('prevents duplicate submissions') }); -
Integration Tests:
- API client function mocking
- Form submission flows
- Navigation between pages
- Search functionality
E2E Tests (Playwright):
- User Journeys:
test('Producer can search and read article', async ({ page }) => { // Login as producer // Navigate to knowledge base // Search for "stripe setup" // Click first result // Verify article displays // Mark as helpful // Verify feedback submitted }); test('Producer can browse by category', async ({ page }) => { // Login as producer // Navigate to knowledge base // Click "Getting Started" category // Verify articles display // Click article // Verify article detail shows });
Target Coverage: - Unit tests: >90% for models, >85% for serializers - Integration tests: >85% for views and API - Frontend tests: >80% for components - E2E tests: Critical user paths (5-10 scenarios)
7.2 Test Environment Requirements¶
Development Environment:
# Backend testing
cd apps/api
python manage.py test knowledge --settings=brktickets.test_settings
# With coverage
coverage run --source='knowledge' manage.py test knowledge
coverage report
coverage html
# Frontend testing
cd apps/producer
npm run test
npm run test:coverage
Test Database: - Separate test database (auto-created by Django) - In-memory SQLite for unit tests (fast) - PostgreSQL for integration tests (realistic)
Test Data: - Fixtures for consistent test data - Factory pattern for dynamic test objects (factory_boy) - Separate test media storage directory
Staging Environment: - Mirror of production configuration - Separate S3 bucket for test media - Full authentication flow testing
7.3 Acceptance Criteria¶
Definition of Done:
- Functionality:
- All models created with proper relationships
- All API endpoints implemented and documented
- All frontend pages functional and responsive
- Search returns relevant results < 500ms
- Image upload and optimization working
- Feedback submission and display working
-
Category navigation and filtering working
-
Code Quality:
- All unit tests passing (>90% model coverage)
- All integration tests passing (>85% view coverage)
- All E2E tests passing (critical paths)
- Code reviewed by at least one team member
- No linting errors (black, flake8)
-
TypeScript strict mode passing
-
Performance:
- Page load time < 2 seconds (measured)
- Search response < 500ms (measured)
- Image file sizes reduced by 40-60%
-
Database query count < 10 per article page
-
Security:
- All endpoints require authentication
- CSRF protection on forms
- XSS prevention verified
- Rate limiting tested
- Image upload validation working
-
No SQL injection vulnerabilities
-
Accessibility:
- WCAG 2.1 AA compliance verified
- Keyboard navigation working
- Screen reader tested
- All images have alt text
- Color contrast ratios met
-
Automated accessibility tests passing
-
Documentation:
- API documentation complete
- README with setup instructions
- Content management guide
- Inline code comments for complex logic
-
Architecture documentation updated
-
Deployment:
- Migrations run successfully on staging
- All tests passing on staging
- No breaking changes to existing features
- Rollback plan documented
- Monitoring and alerts configured
- Sample content loaded
User Acceptance Criteria:
- As a producer, I can:
- Browse knowledge base categories
- Search for articles by keyword
- Read articles with images and formatting
- Mark articles as helpful/not helpful
- Navigate related articles
- Bookmark articles for later (should-have)
-
Access knowledge base on mobile
-
As an administrator, I can:
- Create/edit/delete articles via admin
- Upload and optimize images
- Organize articles into categories
- View analytics on article performance
- Publish/unpublish articles
-
Manage hierarchical categories
-
As a support team member, I can:
- Create articles with rich text editor
- Add tags for cross-referencing
- Set featured articles
- View which articles are most helpful
- See popular search terms
Performance Acceptance: - 95th percentile page load < 2.5 seconds - 99th percentile search response < 750ms - Support 100 concurrent users without degradation - Zero critical errors in first week
Business Acceptance: - 50% of active producers access knowledge base in first month - Average article helpfulness rating > 4.0/5.0 - 30% reduction in support tickets within 3 months - Positive user feedback in surveys
8. Risk Assessment & Mitigation¶
8.1 Technical Risks¶
| Risk | Probability | Impact | Mitigation Strategy |
|---|---|---|---|
| PostgreSQL full-text search insufficient for large scale | Medium | Medium | Start with PostgreSQL, add django-watson if search quality degrades. Elasticsearch is fallback for >10K articles. Monitor search quality metrics. |
| Image processing performance bottleneck | Low | Medium | Process images asynchronously with Celery. Use Pillow optimization settings. Consider external service (Cloudinary) if issues persist. |
| S3 costs exceed budget | Low | Medium | Implement image compression. Monitor usage monthly. Add image size limits. |
| CKEditor5 XSS vulnerabilities | Low | High | Use built-in CKEditor5 sanitization. Add DOMPurify on frontend. Regular security updates. Content Security Policy headers. |
| Database migration issues | Medium | High | Test migrations on staging first. Create rollback scripts. Schedule maintenance window. Have database backup ready. |
| Authentication integration issues with NextAuth | Low | High | Use existing JWT pattern from portal. Test thoroughly with producer accounts. Have fallback session auth. |
| Rich text rendering breaks frontend | Medium | Medium | Sanitize HTML server-side and client-side. Test with malformed content. Add error boundaries. Limit content length. |
8.2 Resource Risks¶
| Risk | Probability | Impact | Mitigation Strategy |
|---|---|---|---|
| Timeline delays due to complexity | Medium | Medium | Build MVP first (Phase 1-3), defer nice-to-have features. Use existing patterns from codebase. Regular progress check-ins. |
| Developer availability/capacity | Medium | High | Clear task breakdown. Prioritize critical features. Can pause after Phase 3 for MVP. Document decisions for handoff. |
| Content creation bottleneck | High | Medium | Create 20-30 starter articles (not 1000+). Train support team on content creation. Use placeholder content for testing. |
| Design/UX resources unavailable | Low | Medium | Use existing Shadcn UI components from producer portal. Follow established patterns. Iterate based on user feedback. |
| Testing resources limited | Medium | Medium | Automate testing where possible. Focus on critical path E2E tests. Use fixtures for test data. Prioritize security testing. |
8.3 Business Risks¶
| Risk | Probability | Impact | Mitigation Strategy |
|---|---|---|---|
| Low producer adoption | Medium | High | Promote during onboarding. Add to producer dashboard prominently. Track usage metrics. Gather feedback and iterate. |
| Content becomes outdated | High | Medium | Assign content owner. Set review schedule. Add "last updated" dates. Monitor feedback for accuracy issues. |
| Support team doesn't maintain content | Medium | High | Provide training and documentation. Make admin interface intuitive. Show impact metrics. Recognize content contributors. |
| Feature creep beyond MVP | Medium | Medium | Stick to phased approach. Document future enhancements separately. Get stakeholder buy-in on scope. |
8.4 Rollback Strategy¶
Rollback Scenarios:
- Critical bug in production:
- Disable knowledge base routes in URL configuration
- Frontend: Hide navigation link, show "maintenance" message
- Backend: Return 503 for all knowledge endpoints
-
Investigate and fix offline
-
Database migration failure:
- Run prepared rollback migration
- Restore database from backup (if necessary)
- Remove 'knowledge' from INSTALLED_APPS
-
Redeploy previous version
-
Performance degradation:
- Disable expensive features (search autocomplete, analytics)
- Add rate limiting
- Scale database if needed
Rollback Steps:
# Backend rollback
cd apps/api
# 1. Revert code changes
git revert <commit-hash>
# 2. Rollback migrations (if needed)
python manage.py migrate knowledge zero
# 3. Remove from settings
# Edit settings.py, remove 'knowledge' from INSTALLED_APPS
# 4. Restart services
docker-compose restart api celery_worker
# Frontend rollback
cd apps/producer
# 1. Revert code changes
git revert <commit-hash>
# 2. Rebuild and deploy
npm run build
# Deploy to hosting
Feature Flags (Recommended):
# settings.py
KNOWLEDGE_BASE_ENABLED = os.getenv('KNOWLEDGE_BASE_ENABLED', 'true').lower() == 'true'
# views.py
if not settings.KNOWLEDGE_BASE_ENABLED:
return Response({'error': 'Knowledge base temporarily unavailable'}, status=503)
This allows instant disable without code changes.
9. Deployment & Operations¶
9.1 Deployment Plan¶
Environment Progression:
Development → Staging → Production
(local) (staging) (production)
↓ ↓ ↓
SQLite PostgreSQL PostgreSQL
FileSystem S3 S3
No CDN CloudFront CloudFront
Debug=True Debug=False Debug=False
Pre-Deployment Checklist:
- All tests passing on CI/CD
- Code review approved
- Database migrations tested on staging
- S3 bucket permissions configured
- Environment variables set
- Documentation updated
- Rollback plan documented
- Monitoring configured
- Sample content prepared
- Team notified of deployment
Database Migration Steps:
# 1. Create backup
pg_dump -h $PGHOST -U $PGUSER -d $PGDATABASE > backup_$(date +%Y%m%d_%H%M%S).sql
# 2. Test migration on staging copy
python manage.py migrate --plan
python manage.py migrate knowledge
# 3. Verify tables created
python manage.py dbshell
\dt knowledge_*
# 4. Load sample data
python manage.py loaddata knowledge/fixtures/sample_data.json
# 5. Verify application starts
python manage.py check --deploy
# 6. Run smoke tests
python manage.py test knowledge.tests.test_smoke
Deployment Steps (Staging):
# Backend deployment
cd apps/api
# 1. Pull latest code
git checkout pique-506-producer-portal-knoweldge-base
git pull origin pique-506-producer-portal-knoweldge-base
# 2. Install dependencies
pip install -r requirements.txt
# 3. Collect static files
python manage.py collectstatic --noinput
# 4. Run migrations
python manage.py migrate
# 5. Restart services
docker-compose restart api celery_worker
# 6. Verify deployment
curl -H "Authorization: Bearer $JWT_TOKEN" \
https://staging-api.piquetickets.com/api/v1/portal/knowledge/articles/
# Frontend deployment
cd apps/producer
# 1. Pull latest code
git pull origin pique-506-producer-portal-knoweldge-base
# 2. Install dependencies
npm install
# 3. Build production bundle
npm run build
# 4. Deploy to hosting (Railway/Vercel)
# (follows existing deployment process)
Production Deployment:
- Maintenance Window: Schedule 1-hour window during low traffic (3-4 AM)
- Feature Flag: Enable knowledge base behind flag initially
- Gradual Rollout:
- Day 1: 10% of producers
- Day 3: 50% of producers
- Day 7: 100% of producers
- Monitoring: Watch error rates, response times, database load
- Announcement: Email producers with link to knowledge base
9.2 Monitoring & Observability¶
Key Metrics to Monitor:
- Application Metrics:
- Request rate (requests/minute)
- Error rate (errors/minute)
- Response time (p50, p95, p99)
-
Database query time
-
Business Metrics:
- Daily active users
- Articles viewed per session
- Search queries per day
- Feedback submission rate
- Most viewed articles
- Most helpful articles
-
Failed searches (no results)
-
Infrastructure Metrics:
- Database connections (current/max)
- S3 bandwidth usage
- API response time distribution
Monitoring Implementation:
# Django middleware for metrics
class KnowledgeMetricsMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
if request.path.startswith('/api/v1/portal/knowledge/'):
start_time = time.time()
response = self.get_response(request)
duration = time.time() - start_time
# Log metrics
logger.info(f"knowledge_request", extra={
'path': request.path,
'method': request.method,
'status': response.status_code,
'duration_ms': duration * 1000,
'user_id': request.user.id if request.user.is_authenticated else None
})
return response
return self.get_response(request)
Logging Configuration:
# settings.py
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'json': {
'()': 'pythonjsonlogger.jsonlogger.JsonFormatter',
'format': '%(asctime)s %(name)s %(levelname)s %(message)s'
}
},
'handlers': {
'knowledge': {
'class': 'logging.handlers.RotatingFileHandler',
'filename': 'logs/knowledge.log',
'maxBytes': 15728640, # 15MB
'backupCount': 10,
'formatter': 'json',
},
},
'loggers': {
'knowledge': {
'handlers': ['knowledge'],
'level': 'INFO',
'propagate': False,
},
},
}
Alerting Rules:
# Example alerting configuration
alerts:
- name: KnowledgeBaseHighErrorRate
condition: error_rate > 5%
duration: 5m
severity: critical
channels: [slack, email]
- name: KnowledgeBaseSlowSearch
condition: search_p95 > 1000ms
duration: 10m
severity: warning
channels: [slack]
- name: KnowledgeBaseDatabaseConnections
condition: db_connections > 80% of max
duration: 5m
severity: critical
channels: [slack, pagerduty]
- name: KnowledgeBaseNoArticleViews
condition: article_views = 0
duration: 1h
severity: warning
channels: [slack]
Dashboard Configuration (Example - Grafana):
# Knowledge Base Overview Dashboard
panels:
- title: "Requests per Minute"
query: rate(knowledge_requests_total[5m])
type: graph
- title: "Response Time (p95)"
query: histogram_quantile(0.95, knowledge_response_time)
type: gauge
- title: "Top Articles"
query: topk(10, knowledge_article_views)
type: table
- title: "Search Success Rate"
query: (knowledge_searches_with_results / knowledge_searches_total) * 100
type: stat
9.3 Support & Maintenance¶
Ongoing Maintenance Tasks:
| Task | Frequency | Owner | Estimated Time |
|---|---|---|---|
| Review and update outdated articles | Monthly | Content Team | 4 hours |
| Analyze search metrics for gaps | Weekly | Product Manager | 1 hour |
| Review feedback comments | Weekly | Support Team | 2 hours |
| Monitor performance metrics | Daily | Engineering | 30 minutes |
| Update featured articles | Monthly | Content Team | 1 hour |
| Cleanup orphaned images | Quarterly | Engineering | 2 hours |
| Security dependency updates | Monthly | Engineering | 2 hours |
| Database performance tuning | Quarterly | Engineering | 4 hours |
Training Requirements:
- Content Creators (Support Team):
- Django admin interface walkthrough
- CKEditor5 usage and best practices
- Image optimization guidelines
- Category and tag management
-
Duration: 2-hour workshop
-
Administrators:
- Article approval workflow
- Analytics dashboard interpretation
- User feedback management
- Content moderation
-
Duration: 1-hour workshop
-
Producers (End Users):
- Knowledge base tour (video)
- How to search effectively
- How to provide feedback
- Duration: 5-minute video tutorial
Documentation Requirements:
- User Documentation:
- Knowledge base user guide (producers)
- FAQ for common questions
-
Video tutorials for key features
-
Admin Documentation:
- Content management guide
- Article writing best practices
- Image optimization guide
-
Analytics interpretation guide
-
Technical Documentation:
- API documentation (auto-generated from DRF)
- Architecture overview
- Database schema
- Deployment procedures
- Troubleshooting guide
Troubleshooting Guide (Quick Reference):
Problem: Search returns no results
- Check article status (must be 'published')
- Verify search index is built
- Check for typos in search query
- Review search query logs
Problem: Images not displaying
- Check S3 bucket permissions
- Verify image path in database
- Check CORS configuration
Problem: Slow page loads
- Check database query count (use debug toolbar)
- Check image file sizes
- Review database indexes
Problem: Feedback not submitting
- Check user authentication
- Verify CSRF token
- Check database constraints
- Review error logs
Problem: Articles not publishing
- Verify all required fields filled
- Check user has publish permissions
- Review validation errors
- Check admin logs
10. Success Measurement¶
10.1 Success Metrics¶
Technical Metrics:
| Metric | Target | Measurement Method | Frequency |
|---|---|---|---|
| Page Load Time (p95) | < 2 seconds | Google Analytics, Lighthouse | Daily |
| Search Response Time (p95) | < 500ms | Backend logging, APM | Daily |
| Database Query Count per Page | < 10 queries | Django Debug Toolbar | Weekly |
| Image File Size Reduction | 40-60% | Before/after comparison | Once |
| Uptime | > 99.9% | Health check monitoring | Continuous |
| Error Rate | < 0.5% | Error tracking (Sentry) | Daily |
| API Response Success Rate | > 99% | API logging | Daily |
User Engagement Metrics:
| Metric | Target | Measurement Method | Frequency |
|---|---|---|---|
| Knowledge Base Monthly Active Users | 80% of active producers | Google Analytics | Monthly |
| Average Articles Read per Session | > 2 articles | Backend analytics | Weekly |
| Search Usage Rate | > 60% of sessions include search | Backend analytics | Weekly |
| Feedback Submission Rate | > 20% of articles read | Backend analytics | Weekly |
| Average Helpfulness Rating | > 4.0/5.0 (80% helpful) | Feedback aggregation | Weekly |
| Bounce Rate | < 40% | Google Analytics | Weekly |
| Time on Page | > 2 minutes (article detail) | Google Analytics | Weekly |
| Return Visit Rate | > 40% within 30 days | User tracking | Monthly |
Business Metrics:
| Metric | Target | Measurement Method | Frequency |
|---|---|---|---|
| Support Ticket Reduction | 50% reduction in basic questions | Support ticket system | Monthly |
| Producer Satisfaction Score | +15% improvement | Producer surveys | Quarterly |
| Content Creation Velocity | 10+ articles per month | Content tracking | Monthly |
| Onboarding Completion Rate | +20% improvement | Onboarding analytics | Monthly |
| Producer Activation Rate | +10% improvement | User analytics | Monthly |
10.2 Review Schedule¶
Short-term Reviews:
- 1 Week Post-Launch:
- Metrics to Review:
- Initial adoption rate (% of producers who accessed)
- Critical errors or bugs
- Page load performance
- Search functionality
- Actions:
- Fix critical bugs
- Gather initial user feedback
-
Attendees: Engineering, Product, Support
-
1 Month Post-Launch:
- Metrics to Review:
- Monthly active users
- Most viewed articles
- Search success rate
- Feedback ratings
- Support ticket trends
- Actions:
- Create content for gaps identified in searches
- Update low-rated articles
- Promote underutilized features
-
Attendees: Engineering, Product, Support, Content Team
-
3 Months Post-Launch:
- Metrics to Review:
- All success metrics
- ROI on support time saved
- Content gaps and opportunities
- Technical performance trends
- User satisfaction surveys
- Actions:
- Full success evaluation
- Identify Phase 2 features
- Content strategy adjustment
- Performance optimization priorities
- Attendees: Engineering, Product, Support, Content Team, Leadership
Ongoing Reviews:
- Weekly: Quick metrics review (10 minutes in team standup)
- Monthly: Detailed analytics review (1-hour meeting)
- Quarterly: Strategic review and roadmap planning (2-hour meeting)
Success Criteria Evaluation:
After 3 months, the knowledge base is considered successful if:
✅ Must Meet (Critical): - 60%+ of active producers have accessed knowledge base - 30%+ reduction in basic support tickets - Average helpfulness rating > 3.5/5.0 - No critical performance or security issues - Page load times < 2.5 seconds (p95)
✅ Should Meet (High Priority): - 80%+ of active producers have accessed knowledge base - 50%+ reduction in basic support tickets - Average helpfulness rating > 4.0/5.0 - 100+ articles published - Search response times < 500ms
✅ Could Meet (Stretch Goals): - 90%+ of active producers have accessed knowledge base - 60%+ reduction in basic support tickets - Average helpfulness rating > 4.5/5.0 - Return visit rate > 50% - Producer satisfaction increase > 20%
11. Appendices¶
Appendix A: Research References¶
Django Documentation: - Django Models - Model design patterns - Django REST Framework - API development - PostgreSQL Full-Text Search - Search implementation - Django Admin Customization - Admin interface
Third-Party Libraries: - CKEditor5 Documentation - Rich text editor - Django Unfold - Admin theme - Pillow Documentation - Image processing - python-slugify - Slug generation
Knowledge Base UX Research: - Knowledge Base Best Practices - Intercom - How to Build a Knowledge Base - Zendesk - Documentation System - Write the Docs
Accessibility: - WCAG 2.1 Guidelines - WebAIM Accessibility Resources
Performance: - Django Performance Optimization - Web.dev Performance Guide
Appendix B: Technical Diagrams¶
Database Schema (Detailed):
-- Category Table
CREATE TABLE knowledge_category (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(100) UNIQUE NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL,
description TEXT,
icon VARCHAR(50),
"order" INTEGER DEFAULT 0,
parent_id BIGINT REFERENCES knowledge_category(id) ON DELETE CASCADE,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_category_slug ON knowledge_category(slug);
CREATE INDEX idx_category_parent ON knowledge_category(parent_id);
CREATE INDEX idx_category_active ON knowledge_category(is_active);
-- Tag Table
CREATE TABLE knowledge_tag (
id BIGSERIAL PRIMARY KEY,
name VARCHAR(50) UNIQUE NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);
CREATE INDEX idx_tag_slug ON knowledge_tag(slug);
-- Article Table
CREATE TABLE knowledge_article (
id BIGSERIAL PRIMARY KEY,
title VARCHAR(200) NOT NULL,
slug VARCHAR(50) UNIQUE NOT NULL,
content TEXT NOT NULL,
excerpt VARCHAR(200),
featured_image VARCHAR(255),
featured_image_alt_text VARCHAR(255),
category_id BIGINT NOT NULL REFERENCES knowledge_category(id) ON DELETE PROTECT,
author_id INTEGER NOT NULL REFERENCES auth_user(id) ON DELETE PROTECT,
status VARCHAR(10) DEFAULT 'draft',
featured BOOLEAN DEFAULT FALSE,
view_count INTEGER DEFAULT 0,
helpful_count INTEGER DEFAULT 0,
not_helpful_count INTEGER DEFAULT 0,
published_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
meta_description VARCHAR(160),
estimated_read_time INTEGER DEFAULT 1
);
CREATE INDEX idx_article_slug ON knowledge_article(slug);
CREATE INDEX idx_article_status_published ON knowledge_article(status, published_at);
CREATE INDEX idx_article_category_status ON knowledge_article(category_id, status);
CREATE INDEX idx_article_view_count ON knowledge_article(view_count DESC);
CREATE INDEX idx_article_helpful_count ON knowledge_article(helpful_count DESC);
CREATE INDEX idx_article_featured ON knowledge_article(featured) WHERE featured = TRUE;
-- Full-text search index
CREATE INDEX idx_article_search ON knowledge_article
USING GIN (to_tsvector('english', title || ' ' || content));
-- Article-Tag Junction Table
CREATE TABLE knowledge_article_tags (
id BIGSERIAL PRIMARY KEY,
article_id BIGINT NOT NULL REFERENCES knowledge_article(id) ON DELETE CASCADE,
tag_id BIGINT NOT NULL REFERENCES knowledge_tag(id) ON DELETE CASCADE,
UNIQUE(article_id, tag_id)
);
CREATE INDEX idx_article_tags_article ON knowledge_article_tags(article_id);
CREATE INDEX idx_article_tags_tag ON knowledge_article_tags(tag_id);
-- ArticleFeedback Table
CREATE TABLE knowledge_articlefeedback (
id BIGSERIAL PRIMARY KEY,
article_id BIGINT NOT NULL REFERENCES knowledge_article(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES auth_user(id) ON DELETE CASCADE,
is_helpful BOOLEAN NOT NULL,
comment TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
UNIQUE(article_id, user_id)
);
CREATE INDEX idx_feedback_article ON knowledge_articlefeedback(article_id);
CREATE INDEX idx_feedback_user ON knowledge_articlefeedback(user_id);
Component Architecture (Frontend):
┌─────────────────────────────────────────────────────────────┐
│ Knowledge Base Page Hierarchy │
└─────────────────────────────────────────────────────────────┘
/dashboard/knowledge/layout.tsx
├── Header
│ ├── Breadcrumbs
│ └── SearchBar (global)
├── [Content Outlet]
│ ├── page.tsx (Home)
│ │ ├── CategoryGrid
│ │ │ └── CategoryCard (multiple)
│ │ ├── FeaturedArticles
│ │ │ └── ArticleCard (carousel)
│ │ ├── RecentArticles
│ │ │ └── ArticleCard (list)
│ │ └── QuickStats
│ │
│ ├── category/[slug]/page.tsx
│ │ ├── CategoryHeader
│ │ ├── FilterControls
│ │ │ ├── SortDropdown
│ │ │ └── ViewToggle
│ │ ├── SubcategoryList
│ │ └── ArticleGrid
│ │ └── ArticleCard (multiple)
│ │
│ ├── article/[slug]/page.tsx
│ │ ├── ArticleHero
│ │ │ └── FeaturedImage
│ │ ├── ArticleContent
│ │ │ ├── TableOfContents (sidebar)
│ │ │ └── RichTextRenderer
│ │ ├── FeedbackWidget
│ │ ├── RelatedArticles
│ │ │ └── ArticleCard (compact)
│ │ └── ShareButtons
│ │
│ └── search/page.tsx
│ ├── SearchFilters (sidebar)
│ ├── ResultsSummary
│ └── SearchResults
│ └── ArticleCard (with highlighting)
└── Footer
Appendix C: Code Examples¶
Example: Article Model with Full Implementation
# apps/api/knowledge/models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import FileExtensionValidator
from django.utils.text import slugify
from PIL import Image
from io import BytesIO
from django.core.files.base import ContentFile
import os
class Category(models.Model):
"""Knowledge base category for organizing articles."""
name = models.CharField(max_length=100, unique=True)
slug = models.SlugField(unique=True, blank=True)
description = models.TextField(blank=True)
icon = models.CharField(max_length=50, blank=True, help_text="FontAwesome icon class")
order = models.IntegerField(default=0)
parent = models.ForeignKey(
'self',
null=True,
blank=True,
on_delete=models.CASCADE,
related_name='subcategories'
)
is_active = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['order', 'name']
verbose_name_plural = 'Categories'
indexes = [
models.Index(fields=['slug']),
models.Index(fields=['parent', 'is_active']),
]
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
@property
def article_count(self):
"""Count of published articles in this category."""
return self.article_set.filter(status='published').count()
class Tag(models.Model):
"""Tag for cross-referencing articles."""
name = models.CharField(max_length=50, unique=True)
slug = models.SlugField(unique=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['name']
def __str__(self):
return self.name
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.name)
super().save(*args, **kwargs)
class Article(models.Model):
"""Knowledge base article."""
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Published'),
('archived', 'Archived'),
]
title = models.CharField(max_length=200)
slug = models.SlugField(unique=True, blank=True)
content = models.TextField()
excerpt = models.TextField(max_length=200, blank=True)
featured_image = models.ImageField(
upload_to='knowledge/articles/',
blank=True,
validators=[FileExtensionValidator(['jpg', 'jpeg', 'png', 'webp'])]
)
featured_image_alt_text = models.CharField(max_length=255, blank=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT)
tags = models.ManyToManyField(Tag, blank=True)
author = models.ForeignKey(User, on_delete=models.PROTECT)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
featured = models.BooleanField(default=False)
view_count = models.PositiveIntegerField(default=0)
helpful_count = models.PositiveIntegerField(default=0)
not_helpful_count = models.PositiveIntegerField(default=0)
published_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
meta_description = models.CharField(max_length=160, blank=True)
estimated_read_time = models.PositiveIntegerField(default=1)
class Meta:
ordering = ['-published_at', '-created_at']
indexes = [
models.Index(fields=['slug']),
models.Index(fields=['status', 'published_at']),
models.Index(fields=['category', 'status']),
models.Index(fields=['-view_count']),
models.Index(fields=['-helpful_count']),
]
def __str__(self):
return self.title
def save(self, *args, **kwargs):
# Generate slug
if not self.slug:
self.slug = self._generate_unique_slug()
# Calculate read time
self.estimated_read_time = self._calculate_read_time()
# Auto-generate excerpt if not provided
if not self.excerpt and self.content:
self.excerpt = self._generate_excerpt()
super().save(*args, **kwargs)
# Process image after save (to have ID)
if self.featured_image:
self._process_featured_image()
def _generate_unique_slug(self):
"""Generate unique slug from title."""
slug = slugify(self.title)
unique_slug = slug
counter = 1
while Article.objects.filter(slug=unique_slug).exists():
unique_slug = f"{slug}-{counter}"
counter += 1
return unique_slug
def _calculate_read_time(self):
"""Calculate estimated read time in minutes (200 words/minute)."""
if not self.content:
return 1
word_count = len(self.content.split())
return max(1, round(word_count / 200))
def _generate_excerpt(self):
"""Generate excerpt from content (first 200 chars)."""
from django.utils.html import strip_tags
plain_text = strip_tags(self.content)
return plain_text[:197] + '...' if len(plain_text) > 200 else plain_text
def _process_featured_image(self):
"""Convert featured image to WebP and optimize."""
if not self.featured_image or self.featured_image.name.endswith('.webp'):
return
try:
img = Image.open(self.featured_image)
# Convert to RGB if necessary
if img.mode in ('RGBA', 'LA', 'P'):
background = Image.new('RGB', img.size, (255, 255, 255))
if img.mode == 'P':
img = img.convert('RGBA')
background.paste(img, mask=img.split()[-1] if img.mode == 'RGBA' else None)
img = background
# Save as WebP
output = BytesIO()
img.save(output, format='WEBP', quality=85, method=6)
output.seek(0)
# Generate new filename
name_without_ext = os.path.splitext(self.featured_image.name)[0]
webp_name = f"{name_without_ext}.webp"
# Save optimized image
self.featured_image.save(
webp_name,
ContentFile(output.read()),
save=False
)
# Save model without triggering infinite recursion
Article.objects.filter(pk=self.pk).update(featured_image=self.featured_image)
except Exception as e:
# Log error but don't fail save
import logging
logger = logging.getLogger(__name__)
logger.error(f"Failed to process image for article {self.pk}: {str(e)}")
def increment_view_count(self):
"""Atomically increment view count."""
Article.objects.filter(pk=self.pk).update(view_count=models.F('view_count') + 1)
self.refresh_from_db()
@property
def helpfulness_percentage(self):
"""Calculate percentage of helpful votes."""
total = self.helpful_count + self.not_helpful_count
if total == 0:
return None
return round((self.helpful_count / total) * 100, 1)
class ArticleFeedback(models.Model):
"""User feedback on article helpfulness."""
article = models.ForeignKey(Article, on_delete=models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
is_helpful = models.BooleanField()
comment = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
unique_together = ['article', 'user']
verbose_name = 'Article Feedback'
verbose_name_plural = 'Article Feedback'
def __str__(self):
return f"Feedback on '{self.article.title}' by {self.user.username}"
def save(self, *args, **kwargs):
is_new = self.pk is None
super().save(*args, **kwargs)
# Update article counts
if is_new:
if self.is_helpful:
Article.objects.filter(pk=self.article.pk).update(
helpful_count=models.F('helpful_count') + 1
)
else:
Article.objects.filter(pk=self.article.pk).update(
not_helpful_count=models.F('not_helpful_count') + 1
)
Example: Article ViewSet with Custom Actions
# apps/api/knowledge/views.py
from rest_framework import viewsets, filters, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q, F
from django.contrib.postgres.search import SearchVector, SearchQuery, SearchRank
from .models import Article, Category, Tag, ArticleFeedback
from .serializers import (
ArticleListSerializer,
ArticleDetailSerializer,
CategorySerializer,
TagSerializer,
FeedbackSerializer
)
from .filters import ArticleFilter
from .permissions import IsProducerUser
class ArticleViewSet(viewsets.ReadOnlyModelViewSet):
"""
ViewSet for knowledge base articles.
List and retrieve published articles.
Supports filtering, searching, and custom actions.
"""
permission_classes = [IsAuthenticated, IsProducerUser]
filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
filterset_class = ArticleFilter
search_fields = ['title', 'content', 'excerpt']
ordering_fields = ['published_at', 'view_count', 'helpful_count']
ordering = ['-published_at']
def get_queryset(self):
"""Return only published articles with optimized queries."""
return Article.objects.filter(
status='published'
).select_related(
'category', 'author'
).prefetch_related(
'tags'
)
def get_serializer_class(self):
"""Use detailed serializer for retrieve, list serializer for list."""
if self.action == 'retrieve':
return ArticleDetailSerializer
return ArticleListSerializer
def retrieve(self, request, *args, **kwargs):
"""Retrieve article and increment view count."""
instance = self.get_object()
instance.increment_view_count()
serializer = self.get_serializer(instance)
return Response(serializer.data)
@action(detail=True, methods=['get'])
def related(self, request, pk=None):
"""Get related articles based on category and tags."""
article = self.get_object()
# Get articles with same category or overlapping tags
related = Article.objects.filter(
status='published'
).exclude(
pk=article.pk
).filter(
Q(category=article.category) | Q(tags__in=article.tags.all())
).distinct().select_related(
'category', 'author'
).prefetch_related(
'tags'
)[:5]
serializer = ArticleListSerializer(related, many=True)
return Response(serializer.data)
@action(detail=True, methods=['post'])
def feedback(self, request, pk=None):
"""Submit feedback on article helpfulness."""
article = self.get_object()
# Check if user already submitted feedback
existing = ArticleFeedback.objects.filter(
article=article,
user=request.user
).first()
if existing:
return Response(
{'error': 'You have already submitted feedback for this article'},
status=status.HTTP_400_BAD_REQUEST
)
# Create feedback
serializer = FeedbackSerializer(data=request.data)
if serializer.is_valid():
serializer.save(article=article, user=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@action(detail=False, methods=['get'])
def featured(self, request):
"""Get featured articles."""
featured = self.get_queryset().filter(featured=True)[:5]
serializer = self.get_serializer(featured, many=True)
return Response(serializer.data)
@action(detail=False, methods=['get'])
def popular(self, request):
"""Get most viewed articles."""
popular = self.get_queryset().order_by('-view_count')[:10]
serializer = self.get_serializer(popular, many=True)
return Response(serializer.data)
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
"""ViewSet for article categories."""
queryset = Category.objects.filter(is_active=True)
serializer_class = CategorySerializer
permission_classes = [IsAuthenticated, IsProducerUser]
lookup_field = 'slug'
@action(detail=True, methods=['get'])
def articles(self, request, slug=None):
"""Get all articles in this category."""
category = self.get_object()
articles = Article.objects.filter(
category=category,
status='published'
).select_related('category', 'author').prefetch_related('tags')
# Apply pagination
page = self.paginate_queryset(articles)
if page is not None:
serializer = ArticleListSerializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = ArticleListSerializer(articles, many=True)
return Response(serializer.data)
class SearchView(viewsets.ViewSet):
"""Custom search view with PostgreSQL full-text search."""
permission_classes = [IsAuthenticated, IsProducerUser]
def list(self, request):
"""
Search articles using full-text search.
Query params:
- q: search query (required)
- category: filter by category slug
- tag: filter by tag slug
"""
query_text = request.query_params.get('q', '').strip()
if not query_text:
return Response(
{'error': 'Search query parameter "q" is required'},
status=status.HTTP_400_BAD_REQUEST
)
# Build search vector and query
vector = SearchVector('title', weight='A') + SearchVector('content', weight='B')
query = SearchQuery(query_text)
# Base queryset
results = Article.objects.filter(
status='published'
).annotate(
rank=SearchRank(vector, query)
).filter(
rank__gte=0.1
).select_related(
'category', 'author'
).prefetch_related(
'tags'
).order_by('-rank')
# Apply filters
category_slug = request.query_params.get('category')
if category_slug:
results = results.filter(category__slug=category_slug)
tag_slug = request.query_params.get('tag')
if tag_slug:
results = results.filter(tags__slug=tag_slug)
# Serialize and return
serializer = ArticleListSerializer(results, many=True)
return Response({
'query': query_text,
'count': results.count(),
'results': serializer.data
})
Appendix D: Meeting Notes & Decisions¶
Project Kickoff Meeting - 2025-10-10
Attendees: Product Manager, Engineering Lead, Support Team Lead
Key Decisions: 1. ✅ Approved MVP scope (Phases 1-3) 2. ✅ Start with PostgreSQL full-text search (not Elasticsearch) 3. ✅ Use CKEditor5 (already installed) instead of adding new editor 4. ✅ Target 6-week timeline for Phase 1-3 completion 5. ✅ Create 20-30 initial articles (not 100+) 6. ⏭️ Defer bookmarking, PDF export, and analytics to Phase 2
Action Items:
- [ ] Engineering: Create knowledge app structure
- [ ] Product: Draft initial article list
- [ ] Support: Review article categories proposal
- [ ] Design: Provide feedback on wireframes
Next Meeting: Weekly standup on Mondays at 10am
Quick Checklist for Document Completion¶
Research Phase¶
- Analyzed existing codebase thoroughly
- Researched industry best practices
- Identified all stakeholders and requirements
- Assessed technical constraints and dependencies
Planning Phase¶
- Created detailed implementation plan (6 phases)
- Defined clear acceptance criteria
- Identified and mitigated risks
- Planned testing strategy
Review Phase¶
- Technical review completed (pending)
- Stakeholder approval obtained (pending)
- Resource allocation confirmed (pending)
- Timeline approved by team (pending)
Documentation¶
- All sections completed with specific details
- File references are accurate and current
- External links verified and accessible
- Document reviewed for clarity and completeness
End of Planning Document
Next Steps: 1. Review this document with stakeholders 2. Get approval to proceed with Phase 1 3. Create Jira tickets for each task 4. Begin implementation following Phase 1 plan
Document Status: ✅ Ready for stakeholder review