14 KiB
Survey Analytics and Tracking Implementation
Overview
This document describes the comprehensive survey tracking and analytics system that tracks patient survey engagement throughout their journey. The system provides detailed metrics on survey delivery, opens, completion rates, time to complete, and abandonment patterns.
Features Implemented
1. Survey Instance Tracking
Tracking Fields Added to SurveyInstance Model
open_count: Number of times the survey link was openedlast_opened_at: Timestamp of the most recent survey opentime_spent_seconds: Total time patient spent on the survey in seconds- Enhanced status field: Now includes additional statuses:
sent- Survey has been sent to patientviewed- Patient opened the surveyin_progress- Patient is actively completing the surveycompleted- Patient completed the surveyabandoned- Patient opened but didn't completeexpired- Survey token has expiredcancelled- Survey was cancelled
2. Detailed Event Tracking
New SurveyTracking Model
Tracks granular events throughout the survey lifecycle:
Event Types:
page_view- Patient opened a survey pagesurvey_started- Patient began answering questionsquestion_answered- Patient answered a specific questionsurvey_completed- Patient submitted the surveysurvey_abandoned- Patient left without completingreminder_sent- Reminder was sent to patient
Tracking Data:
time_on_page- Time spent on current page (seconds)total_time_spent- Cumulative time in survey (seconds)current_question- Question number being vieweduser_agent- Browser/device informationip_address- Patient's IP addressdevice_type- Mobile, tablet, desktopbrowser- Chrome, Safari, Firefox, etc.country- Geographic location (if available)city- Geographic location (if available)metadata- Flexible JSON for additional data
3. Analytics Functions
Located in apps/surveys/analytics.py:
get_survey_engagement_stats()
Returns comprehensive engagement metrics:
{
"total_sent": 100,
"total_opened": 75,
"total_completed": 60,
"total_abandoned": 15,
"open_rate": 75.0, # percentage
"completion_rate": 60.0, # percentage
"abandonment_rate": 20.0, # percentage of opened but not completed
"avg_completion_time_minutes": 12.5,
"delivery_breakdown": {
"sms": {"sent": 60, "opened": 45, "completed": 38},
"email": {"sent": 40, "opened": 30, "completed": 22}
}
}
get_patient_survey_timeline(patient_id)
Returns detailed timeline for a specific patient:
[
{
"survey_instance_id": "...",
"survey_name": "Post-Discharge Survey",
"sent_at": "2025-01-20T10:00:00Z",
"opened_at": "2025-01-20T14:30:00Z",
"completed_at": "2025-01-20T14:45:00Z",
"time_to_complete_minutes": 4.5,
"delivery_channel": "sms",
"status": "completed",
"open_count": 1
}
]
get_survey_completion_times()
Returns individual completion times:
[
{
"patient_id": 123,
"patient_name": "John Doe",
"survey_name": "Post-Discharge Survey",
"sent_at": "2025-01-20T10:00:00Z",
"completed_at": "2025-01-20T14:45:00Z",
"time_to_complete_minutes": 4.5,
"delivery_channel": "sms"
}
]
get_survey_abandonment_analysis()
Analyzes abandonment patterns:
{
"total_abandoned": 15,
"avg_time_before_abandonment_minutes": 3.2,
"abandonment_by_question": {
1: 5, # 5 abandoned at question 1
2: 7,
3: 3
},
"abandonment_by_device": {
"mobile": 10,
"desktop": 4,
"tablet": 1
},
"abandonment_by_channel": {
"sms": 8,
"email": 7
}
}
get_hourly_survey_activity()
Shows activity by hour of day:
[
{"hour": 0, "opens": 5, "completions": 2},
{"hour": 1, "opens": 3, "completions": 1},
...
]
4. API Endpoints
All endpoints require authentication (except public survey views).
Survey Analytics
Base path: /api/surveys/api/analytics/
GET engagement_stats/
- Query params:
survey_template_id(optional) - Filter by survey templatehospital_id(optional) - Filter by hospitaldays(optional, default: 30) - Lookback period
GET patient_timeline/
- Query params:
patient_id(required) - Patient identifier
GET completion_times/
- Query params:
survey_template_id(optional)hospital_id(optional)days(optional, default: 30)
GET abandonment_analysis/
- Query params:
survey_template_id(optional)hospital_id(optional)days(optional, default: 30)
GET hourly_activity/
- Query params:
hospital_id(optional)days(optional, default: 7)
GET summary_dashboard/
- Query params:
hospital_id(optional)days(optional, default: 30)
- Returns comprehensive dashboard with all key metrics
Survey Tracking
Base path: /api/surveys/api/tracking/
GET by_survey/
- Query params:
survey_instance_id(required) - Get tracking events for specific survey
Standard list views with filtering:
survey_instance- Filter by survey instanceevent_type- Filter by event typedevice_type- Filter by device typebrowser- Filter by browser
5. Admin Interface
SurveyInstance Admin
-
Enhanced list display showing:
- Open count
- Time spent (human-readable format)
- Color-coded status badges
-
Inline tracking events view
-
Detailed fieldsets for tracking data
SurveyTracking Admin
- New admin page for tracking events
- Filters by event type, device, browser
- Search by IP address, patient name
- Links back to survey instance
6. Public Survey Tracking
When patients access surveys via public links:
- First open: Creates
page_viewevent, updatesopened_at, sets status toviewed - First interaction: Automatically detected via JavaScript, creates
survey_startedevent, sets status toin_progress - Answering questions: Creates
question_answeredevents - Completion: Creates
survey_completedevent, sets status tocompleted, recordscompleted_at - Abandonment: Automatically detected via scheduled task, creates
survey_abandonedevent
Device/browser detection using user-agents library.
7. Automatic Status Detection
in_progress Status (Automatic)
When a patient starts interacting with the survey, the system automatically detects this and updates the status to in_progress:
Implementation Details:
- Frontend: JavaScript tracking in
templates/surveys/public_form.html - Endpoint:
POST /surveys/s/{access_token}/track-start/ - Trigger: First interaction with any question (click, input, or change)
- Tracking: Records
survey_startedevent with timestamp
How it works:
- Patient opens survey link
- JavaScript monitors form for first interaction
- On first interaction, sends POST request to tracking endpoint
- Server updates status to
in_progress - Creates
SurveyTrackingevent with typesurvey_started - Subsequent interactions track question answers
Code Location:
- View:
apps/surveys/public_views.py:track_survey_start() - JavaScript:
templates/surveys/public_form.html(trackSurveyStart function) - URL:
apps/surveys/urls.py
abandoned Status (Automatic)
When a patient opens the survey but doesn't complete it within a configurable time period, the system automatically marks it as abandoned:
Implementation Details:
- Method: Background task (Celery) + Management command
- Default timeframe: 24 hours (configurable)
- Task:
apps.surveys.tasks.mark_abandoned_surveys - Command:
python manage.py mark_abandoned_surveys
Detection Criteria:
- Survey status is
viewedorin_progress - Token hasn't expired
- Last opened at least X hours ago (default: 24)
- Not already completed, expired, or cancelled
How it works:
- Scheduled task runs periodically (recommended: daily)
- Queries surveys matching abandonment criteria
- Updates status to
abandoned - Creates
SurveyTrackingevent with typesurvey_abandoned - Records metadata:
- Time since opening (hours)
- Number of questions answered
- Total time spent
Usage:
# Run manually
python manage.py mark_abandoned_surveys
# With custom hours
python manage.py mark_abandoned_surveys --hours 48
# Dry run (preview without changes)
python manage.py mark_abandoned_surveys --dry-run
# Via Celery (scheduled)
from apps.surveys.tasks import mark_abandoned_surveys
mark_abandoned_surveys.delay(hours=24)
Configuration: Add to Django settings:
# Number of hours before marking survey as abandoned
SURVEY_ABANDONMENT_HOURS = 24
Celery Beat Schedule:
# In config/celery.py
app.conf.beat_schedule = {
'mark-abandoned-surveys': {
'task': 'apps.surveys.tasks.mark_abandoned_surveys',
'schedule': crontab(hour=2, minute=0), # Run daily at 2 AM
'kwargs': {'hours': 24}
}
}
Code Locations:
- Task:
apps/surveys/tasks.py:mark_abandoned_surveys() - Command:
apps/surveys/management/commands/mark_abandoned_surveys.py
Tracking Flow
Survey Send
JourneyStage → create_survey_instance()
↓
SurveyInstance created with:
- status = 'sent'
- sent_at = now()
- open_count = 0
- time_spent_seconds = 0
Survey Open
Patient opens link → survey_form() view
↓
Create SurveyTracking(event_type='page_view')
↓
Update SurveyInstance:
- open_count += 1
- last_opened_at = now()
- status = 'viewed'
Survey Progress
Patient answers questions
↓
Create SurveyTracking(event_type='question_answered')
↓
Update SurveyInstance:
- status = 'in_progress'
- time_spent_seconds = cumulative time
Survey Completion
Patient submits survey → thank_you() view
↓
Create SurveyTracking(event_type='survey_completed')
↓
Update SurveyInstance:
- status = 'completed'
- completed_at = now()
- total_score calculated
Survey Abandonment
Survey sent → not opened after 24h → scheduled task
↓
Update SurveyInstance:
- status = 'abandoned'
- Create SurveyTracking(event_type='survey_abandoned')
Usage Examples
Get Engagement Statistics
from apps.surveys.analytics import get_survey_engagement_stats
stats = get_survey_engagement_stats(
hospital_id=1,
days=30
)
print(f"Open rate: {stats['open_rate']}%")
print(f"Completion rate: {stats['completion_rate']}%")
print(f"Avg completion time: {stats['avg_completion_time_minutes']} minutes")
Get Patient Timeline
from apps.surveys.analytics import get_patient_survey_timeline
timeline = get_patient_survey_timeline(patient_id=123)
for entry in timeline:
print(f"Survey: {entry['survey_name']}")
print(f"Sent: {entry['sent_at']}")
print(f"Completed: {entry['completed_at']}")
print(f"Time to complete: {entry['time_to_complete_minutes']} minutes")
Access via API
# Get engagement stats
curl -H "Authorization: Bearer <token>" \
"http://localhost:8000/api/surveys/api/analytics/engagement_stats/?hospital_id=1&days=30"
# Get patient timeline
curl -H "Authorization: Bearer <token>" \
"http://localhost:8000/api/surveys/api/analytics/patient_timeline/?patient_id=123"
# Get abandonment analysis
curl -H "Authorization: Bearer <token>" \
"http://localhost:8000/api/surveys/api/analytics/abandonment_analysis/?days=30"
Database Schema
SurveyInstance (New Fields)
ALTER TABLE surveys_surveyinstance
ADD COLUMN open_count INTEGER DEFAULT 0,
ADD COLUMN last_opened_at TIMESTAMP,
ADD COLUMN time_spent_seconds INTEGER DEFAULT 0,
MODIFY COLUMN status ENUM('sent', 'viewed', 'in_progress', 'completed', 'abandoned', 'expired', 'cancelled');
SurveyTracking (New Table)
CREATE TABLE surveys_surveytracking (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
survey_instance_id BIGINT NOT NULL REFERENCES surveys_surveyinstance(id),
event_type VARCHAR(20) NOT NULL,
time_on_page INTEGER,
total_time_spent INTEGER DEFAULT 0,
current_question INTEGER,
user_agent TEXT,
ip_address VARCHAR(45),
device_type VARCHAR(50),
browser VARCHAR(50),
country VARCHAR(100),
city VARCHAR(100),
metadata JSON,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_survey_instance_created (survey_instance_id, created_at DESC),
INDEX idx_event_type_created (event_type, created_at DESC),
INDEX idx_ip_address (ip_address)
);
Performance Considerations
Indexing
- Composite index on
survey_instance_idandcreated_atfor fast timeline queries - Index on
event_typefor event-based filtering - Index on
ip_addressfor location-based analysis
Caching
- Consider caching engagement stats for frequently accessed periods
- Implement Redis caching for dashboard data
Data Retention
- SurveyTracking events can be archived after 90 days
- Aggregate daily statistics for long-term reporting
Future Enhancements
- Real-time Analytics: WebSocket integration for live updates
- Geographic Dashboard: Map visualization of survey responses
- Predictive Analytics: ML model to predict completion likelihood
- A/B Testing: Track engagement with different survey designs
- Integration: Export data to analytics platforms (Google Analytics, Mixpanel)
Testing
Run the included test suite:
python manage.py test apps.surveys.tests.test_analytics
Dependencies
Added to requirements.txt:
user-agents==2.2.0- Browser/device detectionua-parser==1.0.1- User agent parsing
Migration
Run the migration:
python manage.py migrate surveys
Support
For questions or issues, refer to:
- API documentation:
/api/docs/ - Admin interface:
/admin/surveys/ - Code comments in
apps/surveys/analytics.py