HH/apps/surveys/analytics_views.py
2026-01-24 15:27:30 +03:00

269 lines
8.8 KiB
Python

"""
Survey analytics API views.
Provides endpoints for accessing survey engagement metrics:
- Engagement statistics
- Patient survey timelines
- Completion times
- Abandonment analysis
- Hourly activity
"""
from rest_framework import status, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from .analytics import (
get_survey_engagement_stats,
get_patient_survey_timeline,
get_survey_completion_times,
get_survey_abandonment_analysis,
get_hourly_survey_activity,
)
from .models import SurveyInstance, SurveyTracking
from .serializers import SurveyInstanceSerializer, SurveyTrackingSerializer
class SurveyAnalyticsViewSet(viewsets.ViewSet):
"""
Survey analytics API.
Provides various metrics about survey engagement:
- Engagement rates (open, completion, abandonment)
- Time metrics (time to complete, time spent)
- Patient timelines
- Abandonment patterns
- Hourly activity
"""
permission_classes = [IsAuthenticated]
@action(detail=False, methods=['get'])
def engagement_stats(self, request):
"""
Get overall survey engagement statistics.
Query params:
- survey_template_id: Filter by survey template (optional)
- hospital_id: Filter by hospital (optional)
- days: Number of days to look back (default: 30)
"""
survey_template_id = request.query_params.get('survey_template_id')
hospital_id = request.query_params.get('hospital_id')
days = int(request.query_params.get('days', 30))
try:
stats = get_survey_engagement_stats(
survey_template_id=survey_template_id,
hospital_id=hospital_id,
days=days
)
return Response(stats)
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=False, methods=['get'])
def patient_timeline(self, request):
"""
Get survey timeline for a specific patient.
Query params:
- patient_id: Required - Patient ID
"""
patient_id = request.query_params.get('patient_id')
if not patient_id:
return Response(
{'error': 'patient_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
timeline = get_patient_survey_timeline(patient_id)
return Response({'timeline': timeline})
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=False, methods=['get'])
def completion_times(self, request):
"""
Get individual survey completion times.
Query params:
- survey_template_id: Filter by survey template (optional)
- hospital_id: Filter by hospital (optional)
- days: Number of days to look back (default: 30)
"""
survey_template_id = request.query_params.get('survey_template_id')
hospital_id = request.query_params.get('hospital_id')
days = int(request.query_params.get('days', 30))
try:
completion_times = get_survey_completion_times(
survey_template_id=survey_template_id,
hospital_id=hospital_id,
days=days
)
return Response({'completion_times': completion_times})
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=False, methods=['get'])
def abandonment_analysis(self, request):
"""
Analyze abandoned surveys to identify patterns.
Query params:
- survey_template_id: Filter by survey template (optional)
- hospital_id: Filter by hospital (optional)
- days: Number of days to look back (default: 30)
"""
survey_template_id = request.query_params.get('survey_template_id')
hospital_id = request.query_params.get('hospital_id')
days = int(request.query_params.get('days', 30))
try:
analysis = get_survey_abandonment_analysis(
survey_template_id=survey_template_id,
hospital_id=hospital_id,
days=days
)
return Response(analysis)
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=False, methods=['get'])
def hourly_activity(self, request):
"""
Get survey activity by hour.
Query params:
- hospital_id: Filter by hospital (optional)
- days: Number of days to look back (default: 7)
"""
hospital_id = request.query_params.get('hospital_id')
days = int(request.query_params.get('days', 7))
try:
activity = get_hourly_survey_activity(
hospital_id=hospital_id,
days=days
)
return Response({'activity': activity})
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
@action(detail=False, methods=['get'])
def summary_dashboard(self, request):
"""
Get a comprehensive summary dashboard with all key metrics.
Query params:
- hospital_id: Filter by hospital (optional)
- days: Number of days to look back (default: 30)
"""
hospital_id = request.query_params.get('hospital_id')
days = int(request.query_params.get('days', 30))
try:
# Get engagement stats
engagement = get_survey_engagement_stats(
hospital_id=hospital_id,
days=days
)
# Get abandonment analysis
abandonment = get_survey_abandonment_analysis(
hospital_id=hospital_id,
days=days
)
# Get top 10 fastest completions
fastest_completions = get_survey_completion_times(
hospital_id=hospital_id,
days=days
)[:10]
# Get top 10 slowest completions
slowest_completions = get_survey_completion_times(
hospital_id=hospital_id,
days=days
)[-10:] if len(get_survey_completion_times(hospital_id=hospital_id, days=days)) > 10 else []
return Response({
'engagement': engagement,
'abandonment': abandonment,
'fastest_completions': fastest_completions,
'slowest_completions': slowest_completions,
})
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)
class SurveyTrackingViewSet(viewsets.ReadOnlyModelViewSet):
"""
Survey tracking events API.
View detailed tracking events for surveys:
- Page views
- Completions
- Abandonments
- Device/browser information
"""
queryset = SurveyTracking.objects.select_related(
'survey_instance',
'survey_instance__patient',
'survey_instance__survey_template'
).order_by('-created_at')
serializer_class = SurveyTrackingSerializer
permission_classes = [IsAuthenticated]
filterset_fields = ['survey_instance', 'event_type', 'device_type', 'browser']
search_fields = ['ip_address', 'user_agent']
@action(detail=False, methods=['get'])
def by_survey(self, request):
"""
Get tracking events for a specific survey instance.
Query params:
- survey_instance_id: Required - SurveyInstance ID
"""
survey_instance_id = request.query_params.get('survey_instance_id')
if not survey_instance_id:
return Response(
{'error': 'survey_instance_id is required'},
status=status.HTTP_400_BAD_REQUEST
)
try:
tracking_events = self.queryset.filter(
survey_instance_id=survey_instance_id
)
# Serialize and return
serializer = self.get_serializer(tracking_events, many=True)
return Response(serializer.data)
except Exception as e:
return Response(
{'error': str(e)},
status=status.HTTP_400_BAD_REQUEST
)