HH/docs/SURVEY_TRACKING_IMPLEMENTATION.md
2026-01-24 15:27:30 +03:00

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 opened
  • last_opened_at: Timestamp of the most recent survey open
  • time_spent_seconds: Total time patient spent on the survey in seconds
  • Enhanced status field: Now includes additional statuses:
    • sent - Survey has been sent to patient
    • viewed - Patient opened the survey
    • in_progress - Patient is actively completing the survey
    • completed - Patient completed the survey
    • abandoned - Patient opened but didn't complete
    • expired - Survey token has expired
    • cancelled - 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 page
  • survey_started - Patient began answering questions
  • question_answered - Patient answered a specific question
  • survey_completed - Patient submitted the survey
  • survey_abandoned - Patient left without completing
  • reminder_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 viewed
  • user_agent - Browser/device information
  • ip_address - Patient's IP address
  • device_type - Mobile, tablet, desktop
  • browser - 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 template
    • hospital_id (optional) - Filter by hospital
    • days (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 instance
  • event_type - Filter by event type
  • device_type - Filter by device type
  • browser - 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:

  1. First open: Creates page_view event, updates opened_at, sets status to viewed
  2. First interaction: Automatically detected via JavaScript, creates survey_started event, sets status to in_progress
  3. Answering questions: Creates question_answered events
  4. Completion: Creates survey_completed event, sets status to completed, records completed_at
  5. Abandonment: Automatically detected via scheduled task, creates survey_abandoned event

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_started event with timestamp

How it works:

  1. Patient opens survey link
  2. JavaScript monitors form for first interaction
  3. On first interaction, sends POST request to tracking endpoint
  4. Server updates status to in_progress
  5. Creates SurveyTracking event with type survey_started
  6. 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 viewed or in_progress
  • Token hasn't expired
  • Last opened at least X hours ago (default: 24)
  • Not already completed, expired, or cancelled

How it works:

  1. Scheduled task runs periodically (recommended: daily)
  2. Queries surveys matching abandonment criteria
  3. Updates status to abandoned
  4. Creates SurveyTracking event with type survey_abandoned
  5. 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_id and created_at for fast timeline queries
  • Index on event_type for event-based filtering
  • Index on ip_address for 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

  1. Real-time Analytics: WebSocket integration for live updates
  2. Geographic Dashboard: Map visualization of survey responses
  3. Predictive Analytics: ML model to predict completion likelihood
  4. A/B Testing: Track engagement with different survey designs
  5. 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 detection
  • ua-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