Activity Timeline - User Guide
Overview
The Activity Timeline provides a unified feed of all user activities across the CODITECT Dev Context platform. It aggregates activities from different sources (sessions, commits, tasks, messages, files) into a single, chronologically-ordered timeline.
Purpose:
- Track user productivity and activity patterns
- Provide context for collaboration and handoffs
- Generate activity reports and analytics
- Enable activity-based notifications and alerts
Table of Contents
- Model Structure
- Activity Types
- API Endpoints
- Usage Examples
- Creating Timeline Entries
- Filtering and Querying
- Integration with Existing Models
- Performance Considerations
Model Structure
ActivityTimeline Model
Location: backend/models.py
Fields:
id- UUID primary keytenant- ForeignKey to Tenant (multi-tenant isolation)user- ForeignKey to User (who performed the activity)project- ForeignKey to Project (optional, if activity is project-specific)activity_type- CharField (type of activity, see Activity Types)content_type- ForeignKey to ContentType (polymorphic reference to source model)object_id- UUID (ID of the source object)title- CharField(500) (human-readable title)description- TextField (detailed description, optional)timestamp- DateTimeField (when the activity occurred)metadata- JSONField (additional activity-specific data)created_at- DateTimeField (when the timeline entry was created)
Properties:
activity_icon- Returns icon name for frontend displayactivity_color- Returns color class for frontend display
Indexes:
(tenant, timestamp)- Primary query pattern(user, timestamp)- User-specific timelines(project, timestamp)- Project-specific timelines(activity_type, timestamp)- Filter by activity type(content_type, object_id)- Lookup source object
Activity Types
The following activity types are currently supported:
Session Activities
session_start- Session Startedsession_end- Session Ended
Git Activities
commit- Git Commitbranch_create- Branch Createdbranch_merge- Branch Merged
Task Activities
task_created- Task Createdtask_completed- Task Completedtask_updated- Task Updatedtask_deleted- Task Deleted
Communication Activities
message_sent- Message Sentconversation_started- Conversation Started
Project Activities
project_created- Project Createdproject_updated- Project Updated
File Activities
file_created- File Createdfile_modified- File Modifiedfile_deleted- File Deleted
Command Activities
command_executed- Command Executed
API Endpoints
Base URL: /api/v1/activities/
List Activities
GET /api/v1/activities/
Query Parameters:
project- Filter by project IDuser- Filter by user IDactivity_type- Filter by activity type (e.g.,commit,task_completed)date_from- Filter activities from this date (ISO format:2025-01-15)date_to- Filter activities until this date (ISO format:2025-01-20)search- Search in title and descriptionordering- Order by field (e.g.,-timestampfor newest first)
Response:
{
"count": 150,
"next": "/api/v1/activities/?page=2",
"previous": null,
"results": [
{
"id": "uuid-here",
"tenant": "uuid-here",
"user": "uuid-here",
"user_name": "John Doe",
"user_email": "john@example.com",
"project": "uuid-here",
"project_name": "My Project",
"activity_type": "commit",
"activity_type_display": "Git Commit",
"activity_icon": "git-commit",
"activity_color": "blue",
"title": "feat: Add user authentication",
"description": "Implemented JWT-based authentication with refresh tokens",
"timestamp": "2025-01-15T14:30:00Z",
"metadata": {
"commit_hash": "abc123",
"files_changed": 5,
"lines_added": 150,
"lines_removed": 20
},
"related_object_details": {
"type": "commit",
"commit_hash": "abc123",
"message": "feat: Add user authentication",
"author": "John Doe"
},
"created_at": "2025-01-15T14:31:00Z"
}
]
}
Get Today's Activities
GET /api/v1/activities/today/
Response:
{
"count": 15,
"date": "2025-01-15",
"activities": [...]
}
Get This Week's Activities
GET /api/v1/activities/week/
Response:
{
"count": 87,
"week_start": "2025-01-13",
"week_end": "2025-01-19",
"activities": [...]
}
Get Activities by Project
GET /api/v1/activities/by_project/{project_id}/
Response:
{
"project_id": "uuid-here",
"total_count": 42,
"by_type": [
{"activity_type": "commit", "count": 15},
{"activity_type": "task_completed", "count": 12},
{"activity_type": "message_sent", "count": 10},
{"activity_type": "session_start", "count": 5}
],
"activities": [...]
}
Get Activity Statistics
GET /api/v1/activities/stats/
Response:
{
"total_activities": 1542,
"recent_7_days": 87,
"by_type": [
{"activity_type": "message_sent", "count": 450},
{"activity_type": "commit", "count": 320},
{"activity_type": "task_completed", "count": 180}
],
"top_users": [
{
"user_id": "uuid",
"user__email": "john@example.com",
"user__first_name": "John",
"user__last_name": "Doe",
"count": 250
}
],
"top_projects": [
{
"project_id": "uuid",
"project__name": "Backend API",
"count": 450
}
]
}
Usage Examples
Frontend Integration (JavaScript/TypeScript)
// Fetch today's activities
async function getTodayActivities() {
const response = await fetch('/api/v1/activities/today/', {
headers: {
'Authorization': `Bearer ${accessToken}`,
}
});
const data = await response.json();
return data.activities;
}
// Filter activities by project and date range
async function getProjectActivities(projectId, dateFrom, dateTo) {
const params = new URLSearchParams({
project: projectId,
date_from: dateFrom,
date_to: dateTo,
ordering: '-timestamp'
});
const response = await fetch(`/api/v1/activities/?${params}`, {
headers: {
'Authorization': `Bearer ${accessToken}`,
}
});
return response.json();
}
// Get activity statistics
async function getActivityStats() {
const response = await fetch('/api/v1/activities/stats/', {
headers: {
'Authorization': `Bearer ${accessToken}`,
}
});
return response.json();
}
Python Client
import requests
from datetime import datetime, timedelta
class ActivityTimelineClient:
def __init__(self, base_url, token):
self.base_url = base_url
self.headers = {'Authorization': f'Bearer {token}'}
def get_activities(self, **filters):
"""Get activities with optional filters"""
response = requests.get(
f'{self.base_url}/api/v1/activities/',
params=filters,
headers=self.headers
)
return response.json()
def get_today(self):
"""Get today's activities"""
response = requests.get(
f'{self.base_url}/api/v1/activities/today/',
headers=self.headers
)
return response.json()
def get_user_activities(self, user_id, days=7):
"""Get user activities for the last N days"""
date_from = (datetime.now() - timedelta(days=days)).isoformat()
return self.get_activities(user=user_id, date_from=date_from)
def get_project_activities(self, project_id):
"""Get all activities for a project"""
response = requests.get(
f'{self.base_url}/api/v1/activities/by_project/{project_id}/',
headers=self.headers
)
return response.json()
# Usage
client = ActivityTimelineClient('https://api.coditect.ai', 'your-token')
activities = client.get_today()
print(f"Today's activities: {activities['count']}")
Creating Timeline Entries
Manual Creation
from django.contrib.contenttypes.models import ContentType
from backend.models import ActivityTimeline, GitCommit, User, Project
# Example: Create activity when a git commit is made
commit = GitCommit.objects.get(commit_hash='abc123')
user = User.objects.get(email='john@example.com')
project = Project.objects.get(name='My Project')
ActivityTimeline.objects.create(
tenant=user.tenant,
user=user,
project=project,
activity_type='commit',
content_type=ContentType.objects.get_for_model(GitCommit),
object_id=commit.id,
title=f"Committed: {commit.message[:50]}",
description=commit.message,
timestamp=commit.committed_at,
metadata={
'commit_hash': commit.commit_hash,
'files_changed': commit.files_changed,
'lines_added': commit.lines_added,
'lines_removed': commit.lines_removed,
}
)
Using Django Signals (Recommended)
Location: backend/signals.py (create this file)
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.contenttypes.models import ContentType
from .models import (
GitCommit, Task, Session, Message, Project,
ActivityTimeline
)
@receiver(post_save, sender=GitCommit)
def create_commit_activity(sender, instance, created, **kwargs):
"""Create timeline entry when a commit is created"""
if created:
ActivityTimeline.objects.create(
tenant=instance.repository.tenant,
user=instance.repository.tenant.user_set.first(), # Or get from commit metadata
project=instance.repository.projects.first(),
activity_type='commit',
content_type=ContentType.objects.get_for_model(GitCommit),
object_id=instance.id,
title=f"Committed: {instance.message[:50]}",
description=instance.message,
timestamp=instance.committed_at,
metadata={
'commit_hash': instance.commit_hash,
'files_changed': instance.files_changed,
}
)
@receiver(post_save, sender=Task)
def create_task_activity(sender, instance, created, **kwargs):
"""Create timeline entry for task creation/completion"""
if created:
activity_type = 'task_created'
title = f"Created task: {instance.title}"
elif instance.current_status and instance.current_status.status == 'completed':
activity_type = 'task_completed'
title = f"Completed task: {instance.title}"
else:
activity_type = 'task_updated'
title = f"Updated task: {instance.title}"
ActivityTimeline.objects.create(
tenant=instance.tenant,
user=instance.created_by,
project=instance.task_list.project,
activity_type=activity_type,
content_type=ContentType.objects.get_for_model(Task),
object_id=instance.id,
title=title,
description=instance.description,
timestamp=instance.updated_at,
metadata={
'priority': instance.priority,
'status': instance.current_status.status if instance.current_status else None,
}
)
@receiver(post_save, sender=Session)
def create_session_activity(sender, instance, created, **kwargs):
"""Create timeline entry for session start/end"""
if created:
ActivityTimeline.objects.create(
tenant=instance.tenant,
user=instance.user,
project=instance.project,
activity_type='session_start',
content_type=ContentType.objects.get_for_model(Session),
object_id=instance.id,
title=f"Started session: {instance.title}",
description=instance.description,
timestamp=instance.started_at,
)
elif instance.ended_at:
ActivityTimeline.objects.create(
tenant=instance.tenant,
user=instance.user,
project=instance.project,
activity_type='session_end',
content_type=ContentType.objects.get_for_model(Session),
object_id=instance.id,
title=f"Ended session: {instance.title}",
description=instance.description,
timestamp=instance.ended_at,
metadata={
'duration_minutes': (instance.ended_at - instance.started_at).total_seconds() / 60,
}
)
Register signals in backend/apps.py:
from django.apps import AppConfig
class BackendConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'backend'
verbose_name = 'CODITECT Backend'
def ready(self):
# Import signals to register them
import backend.signals # noqa
Filtering and Querying
Filter by Multiple Activity Types
# Get all task-related activities
activities = ActivityTimeline.objects.filter(
activity_type__in=['task_created', 'task_completed', 'task_updated']
)
Filter by Date Range
from datetime import datetime, timedelta
# Get activities from the last 7 days
seven_days_ago = datetime.now() - timedelta(days=7)
activities = ActivityTimeline.objects.filter(
timestamp__gte=seven_days_ago
)
Filter by User and Project
# Get user's activities on a specific project
activities = ActivityTimeline.objects.filter(
user_id=user_id,
project_id=project_id
).order_by('-timestamp')
Activity Count by Type
from django.db.models import Count
# Get activity counts grouped by type
activity_counts = ActivityTimeline.objects.values('activity_type').annotate(
count=Count('id')
).order_by('-count')
Integration with Existing Models
Session Model
class Session(models.Model):
# ... existing fields ...
def create_timeline_entry(self, activity_type):
"""Helper method to create timeline entry"""
from django.contrib.contenttypes.models import ContentType
ActivityTimeline.objects.create(
tenant=self.tenant,
user=self.user,
project=self.project,
activity_type=activity_type,
content_type=ContentType.objects.get_for_model(Session),
object_id=self.id,
title=f"{activity_type.replace('_', ' ').title()}: {self.title}",
description=self.description,
timestamp=self.ended_at if activity_type == 'session_end' else self.started_at,
)
Task Model
class Task(models.Model):
# ... existing fields ...
def mark_completed(self):
"""Mark task as completed and create timeline entry"""
self.current_status.status = 'completed'
self.current_status.save()
ActivityTimeline.objects.create(
tenant=self.tenant,
user=self.created_by,
project=self.task_list.project,
activity_type='task_completed',
content_type=ContentType.objects.get_for_model(Task),
object_id=self.id,
title=f"Completed: {self.title}",
timestamp=timezone.now(),
)
Performance Considerations
Database Indexes
The ActivityTimeline model has comprehensive indexes for common query patterns:
indexes = [
models.Index(fields=['tenant', '-timestamp']), # Most common: tenant timeline
models.Index(fields=['user', '-timestamp']), # User-specific timeline
models.Index(fields=['project', '-timestamp']), # Project-specific timeline
models.Index(fields=['activity_type', '-timestamp']), # Filter by type
models.Index(fields=['content_type', 'object_id']), # Lookup source object
]
Query Optimization
Use select_related for foreign keys:
# Good - One query with JOIN
activities = ActivityTimeline.objects.select_related(
'user', 'project', 'content_type'
).filter(timestamp__gte=seven_days_ago)
# Bad - N+1 queries
activities = ActivityTimeline.objects.filter(
timestamp__gte=seven_days_ago
) # Each access to activity.user triggers a query
Pagination
Always paginate large result sets:
from django.core.paginator import Paginator
activities = ActivityTimeline.objects.all().order_by('-timestamp')
paginator = Paginator(activities, 50) # 50 items per page
page = paginator.get_page(1)
Archiving Old Activities
Consider archiving or deleting very old timeline entries to keep the table size manageable:
from datetime import datetime, timedelta
# Archive activities older than 1 year
one_year_ago = datetime.now() - timedelta(days=365)
ActivityTimeline.objects.filter(
timestamp__lt=one_year_ago
).delete()
Future Enhancements
Potential future improvements to the Activity Timeline:
- Real-time Updates - WebSocket push for live activity feed
- Activity Aggregation - Group similar activities (e.g., "5 commits in the last hour")
- Smart Notifications - AI-based activity pattern detection and alerts
- Activity Search - Full-text search across activity titles and descriptions
- Activity Export - Export timeline to CSV, PDF, or calendar formats
- Collaboration Insights - Team activity heatmaps and collaboration graphs
- Activity-based Recommendations - Suggest tasks based on activity patterns
Version: 2.0.0 Last Updated: 2025-11-26 Maintainer: AZ1.AI Development Team