API.md now lists the supported custom RRULE patterns (every Wednesday, every other Wednesday, every second Tuesday, every 15th, etc.) matching the newly implemented dateutil.rrule evaluation. CLAUDE.md's Task model reference also gets corrected: it listed recurrence_type/recurrence_interval fields that never existed on the model. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
16 KiB
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Development Commands
Local Development (Non-Docker)
# Set environment for development
export DJANGO_SETTINGS_MODULE=config.settings.development
# Create and activate virtual environment
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
# Install dependencies
pip install -r requirements.txt
# Database operations
python manage.py migrate
python manage.py makemigrations
python manage.py createsuperuser
# Run development server
python manage.py runserver
# Run tests
python manage.py test
# Django shell
python manage.py shell
# Collect static files (for production)
python manage.py collectstatic --noinput
Docker Development
# Development with live reload
make dev
make dev-build # Rebuild containers
# Production mode
make prod
make build # Build production containers
make deploy # Full deployment (pull, build, migrate, restart)
# Database operations
make migrate
make makemigrations
make createsuperuser
make dbshell # Access database shell
make backup # Backup database
# Monitoring
make logs # All container logs
make logs-web # Web container logs only
make logs-celery # Celery worker logs
make status # Container status
# Control
make stop
make restart
make clean # Stop and remove all containers/volumes
# Shell access
make shell # Django shell
make test # Run tests in container
Celery (Background Tasks)
# Local development (non-Docker)
# Terminal 1: Run Celery worker
celery -A config worker -l info
# Terminal 2: Run Celery beat (scheduled tasks)
celery -A config beat -l info
# In Docker, Celery runs automatically as separate services
High-Level Architecture
Django Apps
users/ - User authentication and management
- Custom User model with email-based authentication (no username)
- Three-tier verification: email verification → admin approval → login
- JWT token authentication with refresh tokens
- Device token management for push notifications
- Rate-limited registration (5/hour) and login (10/hour)
tasks/ - Core task management
- Hierarchical tasks with parent/subtask relationships
- Task status (pending, in_progress, completed, cancelled) and priority (low, medium, high, urgent)
- Recurrence patterns (daily, weekly, biweekly, monthly, yearly, or custom RRULE for day-of-week/nth-weekday/day-of-month control)
- Time tracking with start/stop timer functionality
- Tag system with colors and icons for organization
- Task sharing with permission levels (viewer/editor)
- Soft-delete architecture (is_deleted flag) for sync compatibility
sync/ - Mobile app synchronization
- Offline-first bidirectional sync protocol
- Timestamp-based conflict detection with three resolution strategies (local, server, merged)
- Multi-device support with independent sync tokens per device
- Incremental sync (delta updates) and full sync support
- Uses sync_id (UUID) separate from Django id for mobile compatibility
- Soft-delete propagation to enable client-side deletion
notifications/ - Email notifications and reminders
- Daily email digest (overdue + due today tasks)
- Timezone-aware scheduling (6-7 AM in user's local time)
- Deduplication (one email per user per day)
- Celery-based background processing
- Recurring task safety net (creates missed recurrences)
Key Architectural Patterns
UUID Primary Keys - All models use UUID as primary key for distributed system compatibility and security.
Dual ID System - Each entity has both id (Django PK) and sync_id (for mobile sync). Sync protocol uses sync_id to maintain consistency across devices.
Soft Delete Pattern - All entities have is_deleted boolean instead of hard deletes. Dual managers enable filtering:
objects- Default manager (excludes deleted)all_objects- Includes deleted items (used in sync)
Multiple Serializers Per Context - Different serializers for list/detail/sync endpoints to optimize performance:
TaskListSerializer- Lightweight for list viewsTaskSerializer- Full details with nested subtasksTaskSyncSerializer- Uses sync_id, includes is_deleted
Permission System - Custom IsOwnerOrReadOnlyIfShared permission:
- Read access: Owner OR users with shared access
- Write access: Owner only
- Security filtering in serializers prevents tag/task leakage
Custom Authentication Backend - EmailVerifiedApprovedBackend enforces:
- Email verification check
- Admin approval check
- Superusers bypass both checks
- Stores error state in session for web UI
Timezone-Aware Operations - All time calculations use user's timezone:
- Daily emails scheduled in user's local 6-7 AM
- Overdue calculation relative to user's local date
- UTC storage, converted for display and logic
Mobile App Support - AllowMobileAppFramingMiddleware detects mobile app via User-Agent and removes X-Frame-Options to allow WebView embedding while maintaining security for web browsers.
Sync Protocol Details
The sync endpoint (POST /api/sync/) handles offline-first synchronization:
Request Format:
{
"device_id": "unique-device-id",
"last_sync_token": "previous-token-or-null",
"changes": {
"tasks": [...],
"tags": [...],
"time_entries": [...]
}
}
Response Format:
{
"sync_token": "new-sync-token",
"server_time": "2025-01-10T12:00:00Z",
"server_changes": {
"tasks": [...],
"tags": [...],
"time_entries": [...]
},
"conflicts": [...]
}
Conflict Detection: Compares updated_at timestamp between client's last sync and server's current state. If server modified entity since last sync, conflict is flagged.
Conflict Resolution Strategies:
- local - Apply client changes (overwrite server)
- server - Keep server version (discard client)
- merged - Accept manually merged data
Celery Task Schedule
Configured in config/celery.py:
send_daily_task_email - Runs every hour
- Fetches users with email notifications enabled
- Converts UTC to user's timezone
- Sends email only if user's local time is 6-7 AM
- Checks for existing notification to prevent duplicates
- Groups tasks: overdue + due today (pending/in_progress only)
process_recurring_tasks - Runs daily at midnight
- Safety net for recurring task creation
- Processes completions from last 48 hours
- Creates next recurrence if missing
- Prevents duplicate recurrences
Settings Modules
config.settings.development - Local development
- DEBUG=True
- SQLite database
- Console email backend
- Minimal security
config.settings.production - Production deployment
- DEBUG=False
- PostgreSQL required
- SMTP email backend
- Full security (HTTPS, CSP, HSTS)
- Gunicorn with multiple workers
config.settings.selfhosted - Docker/self-hosted
- Environment-based configuration
- PostgreSQL with health checks
- Redis for Celery
- Configurable workers and timeout
- WhiteNoise for static files
Database Models Reference
Task Model Fields:
- Hierarchy:
parent(ForeignKey to self) - Status: pending, in_progress, completed, cancelled
- Priority: low, medium, high, urgent
- Recurrence:
recurrence(none/daily/weekly/biweekly/monthly/yearly/custom),recurrence_rule(RRULE string, used whenrecurrence='custom', parsed viadateutil.rrule),recurrence_end_date - Timing:
due_date,due_time,completed_at,created_at,updated_at - Soft delete:
is_deleted(filters viaSoftDeleteManager) - Sync:
sync_id(UUID for mobile sync) - Relationships:
tags(ManyToMany),time_entries(reverse FK)
User Model Fields:
- Authentication:
email(unique),password,first_name,last_name - Verification:
email_verified,is_approved,approved_by - Preferences:
timezone,reminder_minutes_before,notifications_enabled,email_notifications_enabled - UUID primary key for security
TimeEntry Model:
start_time,end_time,duration(auto-calculated)- Foreign keys:
task,user - Soft delete:
is_deleted
TaskShare Model:
shared_by,shared_with(User FKs)permission_level: viewer, editor- Can share tasks or tags
- Generic relation pattern
API Endpoint Overview
Authentication:
POST /api/users/register/- Create account (rate limited 5/hour)POST /api/users/token/- Login (rate limited 10/hour)POST /api/users/token/refresh/- Refresh JWTPOST /api/users/verify-email/- Verify email tokenPOST /api/users/resend-verification/- Resend verification email
Tasks:
GET/POST /api/tasks/- List/create tasks (supports filtering by status, priority, tags, search)GET/PUT/DELETE /api/tasks/<id>/- Task detail operationsPOST /api/tasks/<id>/start-timer/- Start time trackingPOST /api/tasks/time-entries/<id>/stop/- Stop timerGET/POST /api/tasks/tags/- Tag managementGET/POST /api/tasks/shares/- Share management
Sync:
POST /api/sync/- Main sync endpoint (rate limited 100/hour)GET /api/sync/conflicts/- List pending conflictsPOST /api/sync/conflicts/<id>/resolve/- Resolve conflict
Notifications:
GET /api/notifications/- List notificationsPOST /api/notifications/<id>/read/- Mark as readPOST /api/notifications/mark-all-read/- Mark all readGET /api/notifications/unread-count/- Get unread count
Environment Variables
Key variables (see .env.example and stack.env.example):
Django Core:
SECRET_KEY- Django secret (generate withpython -c "from django.core.management.utils import get_random_secret_key; print(get_random_secret_key())")DEBUG- Debug mode (False in production)ALLOWED_HOSTS- Comma-separated hostsCSRF_TRUSTED_ORIGINS- HTTPS originsSITE_DOMAIN- Domain for email links
Database:
DATABASE_URL- Connection string (e.g.,postgresql://user:pass@host:5432/db)
Redis & Celery:
REDIS_URL- Redis connection (e.g.,redis://localhost:6379/0)
Email:
EMAIL_HOST,EMAIL_PORT,EMAIL_USE_TLSEMAIL_HOST_USER,EMAIL_HOST_PASSWORDDEFAULT_FROM_EMAIL- Sender addressEMAIL_VERIFICATION_TOKEN_EXPIRY_HOURS- Token validity (default: 24)
Docker/Gunicorn:
GUNICORN_WORKERS- Worker processes (default: 3)GUNICORN_TIMEOUT- Request timeout (default: 60)WEB_PORT- External port (default: 8000)
Testing
Tests located in each app's tests.py. Run with:
python manage.py test # All tests
python manage.py test users # Specific app
python manage.py test users.tests.TestUserModel # Specific test
Docker:
make test # Run tests in container
Deployment Notes
Docker Deployment:
- Production uses PostgreSQL (postgres:16-alpine)
- Redis for Celery message broker (redis:7-alpine)
- Web service runs Gunicorn with configurable workers
- Celery worker and beat run as separate services
- Health checks on all services
- Named volumes for persistence (db_data, redis_data, static_files)
- Networks: frontend (web-facing), backend (internal services)
Manual Deployment:
- Use
config.settings.productionsettings module - Requires PostgreSQL (not SQLite)
- Configure SMTP for email
- Use Gunicorn behind Nginx reverse proxy
- Set up systemd services for Gunicorn, Celery worker, Celery beat
- Configure SSL/TLS certificates (Let's Encrypt recommended)
- Enable firewall (UFW) with SSH, HTTP, HTTPS only
Production Checklist:
- Set DEBUG=False
- Use strong SECRET_KEY
- Configure ALLOWED_HOSTS (no wildcards)
- Set CSRF_TRUSTED_ORIGINS for HTTPS
- Use PostgreSQL (not SQLite)
- Configure email backend (SMTP)
- Enable security headers (in settings)
- Set up SSL/TLS certificates
- Configure database backups
- Set up log rotation
- Monitor error logs and uptime
Common Development Patterns
Creating Recurring Tasks: When a recurring task is marked complete, the next recurrence is automatically created in tasks/models.py create_next_recurrence() method, which calls calculate_next_due_date(). For recurrence='custom', the next date is computed by evaluating recurrence_rule (an RRULE string) via dateutil.rrule; the web UI's Custom recurrence builder (in templates/tasks/_recurrence_fields.html and static/js/app.js) generates these strings for day-of-week, nth-weekday, and day-of-month patterns. Celery task process_recurring_tasks() runs as safety net.
Time Entry Workflow:
POST /api/tasks/<id>/start-timer/creates TimeEntry with start_time- Running timer tracked in UI
POST /api/time-entries/<id>/stop/sets end_time and calculates duration
Email Verification Flow:
- User registers →
email_verified=False,is_approved=False - EmailVerificationToken created with 24-hour expiry
- User clicks link →
email_verified=True, token marked used - Admins notified via email
- Admin approves in Django admin →
is_approved=True - User notified and can now login
Adding New Celery Tasks:
- Create task function in app's
tasks.pywith@shared_taskdecorator - Add to beat schedule in
config/celery.pyif periodic - Import in app's
__init__.pyto ensure task registration - Restart Celery worker and beat services
Extending User Model: The User model is in users/models.py. Add fields there, create migration, update serializers and admin if needed.
Security Filtering: When adding new endpoints that query related objects, filter by user in viewset's get_queryset(). Example from TaskViewSet:
def get_queryset(self):
return Task.objects.filter(user=self.request.user)
File Structure Reference
config/ # Django project configuration
settings/ # Environment-specific settings
base.py # Shared settings
development.py # Local development
production.py # Production deployment
selfhosted.py # Docker/self-hosted
celery.py # Celery configuration
urls.py # Root URL configuration
wsgi.py # WSGI entry point
users/ # User management app
models.py # User, DeviceToken, EmailVerificationToken
views.py # Registration, login, profile
serializers.py # User serializers
backends.py # EmailVerifiedApprovedBackend
utils.py # Email utilities
tasks/ # Task management app
models.py # Task, Tag, TimeEntry, TaskShare
views.py # Task CRUD, filtering, timers
serializers.py # Multiple serializers per context
middleware.py # AllowMobileAppFramingMiddleware
permissions.py # IsOwnerOrReadOnlyIfShared
sync/ # Mobile sync app
models.py # SyncLog, SyncConflict
views.py # Sync endpoint, conflict resolution
serializers.py # SyncSerializer with sync_id
notifications/ # Notifications app
models.py # Notification, ScheduledReminder
views.py # Notification CRUD
tasks.py # Celery tasks (daily email, recurring tasks)
templates/ # Django templates
base.html # Base template with dark mode
tasks/ # Task-related templates
users/ # User-related templates
static/ # Static assets
css/ # Stylesheets
js/ # JavaScript
docker/ # Docker-related files
docker-compose.yml # Production Docker setup
docker-compose.dev.yml # Development overrides
Dockerfile # Production image
Dockerfile.dev # Development image
Makefile # Docker command shortcuts