""" Surveys admin """ from django.contrib import admin from django.utils.html import format_html from django.db.models import Count from .models import SurveyInstance, SurveyQuestion, SurveyResponse, SurveyTemplate, SurveyTracking class SurveyQuestionInline(admin.TabularInline): """Inline admin for survey questions""" model = SurveyQuestion extra = 1 fields = ['order', 'text', 'question_type', 'is_required'] ordering = ['order'] @admin.register(SurveyTemplate) class SurveyTemplateAdmin(admin.ModelAdmin): """Survey template admin""" list_display = [ 'name', 'survey_type', 'hospital', 'scoring_method', 'negative_threshold', 'get_question_count', 'is_active' ] list_filter = ['survey_type', 'scoring_method', 'is_active', 'hospital'] search_fields = ['name', 'name_ar'] ordering = ['hospital', 'name'] inlines = [SurveyQuestionInline] fieldsets = ( (None, { 'fields': ('name', 'name_ar') }), ('Configuration', { 'fields': ('hospital', 'survey_type') }), ('Scoring', { 'fields': ('scoring_method', 'negative_threshold') }), ('Status', { 'fields': ('is_active',) }), ('Metadata', { 'fields': ('created_at', 'updated_at') }), ) readonly_fields = ['created_at', 'updated_at'] def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('hospital').prefetch_related('questions') @admin.register(SurveyQuestion) class SurveyQuestionAdmin(admin.ModelAdmin): """Survey question admin""" list_display = [ 'survey_template', 'order', 'text_preview', 'question_type', 'is_required' ] list_filter = ['survey_template', 'question_type', 'is_required'] search_fields = ['text', 'text_ar'] ordering = ['survey_template', 'order'] fieldsets = ( (None, { 'fields': ('survey_template', 'order') }), ('Question Text', { 'fields': ('text', 'text_ar') }), ('Configuration', { 'fields': ('question_type', 'is_required') }), ('Choices (for multiple choice)', { 'fields': ('choices_json',), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('created_at', 'updated_at') }), ) readonly_fields = ['created_at', 'updated_at'] def text_preview(self, obj): """Show preview of question text""" return obj.text[:100] + '...' if len(obj.text) > 100 else obj.text text_preview.short_description = 'Question' def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('survey_template') class SurveyResponseInline(admin.TabularInline): """Inline admin for survey responses""" model = SurveyResponse extra = 0 fields = ['question', 'numeric_value', 'text_value', 'choice_value'] readonly_fields = ['question'] ordering = ['question__order'] def has_add_permission(self, request, obj=None): return False class SurveyTrackingInline(admin.TabularInline): """Inline admin for survey tracking events""" model = SurveyTracking extra = 0 fields = ['event_type', 'device_type', 'browser', 'total_time_spent', 'created_at'] readonly_fields = ['event_type', 'device_type', 'browser', 'total_time_spent', 'created_at'] ordering = ['-created_at'] can_delete = False def has_add_permission(self, request, obj=None): return False @admin.register(SurveyInstance) class SurveyInstanceAdmin(admin.ModelAdmin): """Survey instance admin""" list_display = [ 'survey_template', 'patient', 'encounter_id', 'status_badge', 'delivery_channel', 'open_count', 'time_spent_display', 'total_score', 'is_negative', 'sent_at', 'completed_at' ] list_filter = [ 'status', 'delivery_channel', 'is_negative', 'survey_template__survey_type', 'sent_at', 'completed_at' ] search_fields = [ 'patient__mrn', 'patient__first_name', 'patient__last_name', 'encounter_id', 'access_token' ] ordering = ['-created_at'] inlines = [SurveyResponseInline, SurveyTrackingInline] fieldsets = ( (None, { 'fields': ('survey_template', 'patient', 'encounter_id') }), ('Journey Linkage', { 'fields': ('journey_instance',), 'classes': ('collapse',) }), ('Delivery', { 'fields': ( 'delivery_channel', 'recipient_phone', 'recipient_email', 'access_token', 'token_expires_at' ) }), ('Status & Timestamps', { 'fields': ('status', 'sent_at', 'opened_at', 'completed_at') }), ('Tracking', { 'fields': ('open_count', 'last_opened_at', 'time_spent_seconds') }), ('Scoring', { 'fields': ('total_score', 'is_negative') }), ('Metadata', { 'fields': ('metadata', 'created_at', 'updated_at'), 'classes': ('collapse',) }), ) readonly_fields = [ 'access_token', 'token_expires_at', 'sent_at', 'opened_at', 'completed_at', 'open_count', 'last_opened_at', 'time_spent_seconds', 'total_score', 'is_negative', 'created_at', 'updated_at' ] def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related( 'survey_template', 'patient', 'journey_instance' ).prefetch_related('responses', 'tracking_events') def status_badge(self, obj): """Display status with color badge""" colors = { 'sent': 'secondary', 'viewed': 'info', 'in_progress': 'warning', 'completed': 'success', 'abandoned': 'danger', 'expired': 'secondary', 'cancelled': 'dark', } color = colors.get(obj.status, 'secondary') return format_html( '{}', color, obj.get_status_display() ) status_badge.short_description = 'Status' def time_spent_display(self, obj): """Display time spent in human-readable format""" if obj.time_spent_seconds: minutes = obj.time_spent_seconds // 60 seconds = obj.time_spent_seconds % 60 return f"{minutes}m {seconds}s" return '-' time_spent_display.short_description = 'Time Spent' @admin.register(SurveyTracking) class SurveyTrackingAdmin(admin.ModelAdmin): """Survey tracking admin""" list_display = [ 'survey_instance_link', 'event_type_badge', 'device_type', 'browser', 'ip_address', 'total_time_spent_display', 'created_at' ] list_filter = [ 'event_type', 'device_type', 'browser', 'survey_instance__survey_template', 'created_at' ] search_fields = [ 'survey_instance__patient__mrn', 'survey_instance__patient__first_name', 'survey_instance__patient__last_name', 'ip_address', 'user_agent' ] ordering = ['-created_at'] fieldsets = ( (None, { 'fields': ('survey_instance', 'event_type') }), ('Timing', { 'fields': ('time_on_page', 'total_time_spent') }), ('Context', { 'fields': ('current_question',) }), ('Device Info', { 'fields': ('user_agent', 'ip_address', 'device_type', 'browser') }), ('Location', { 'fields': ('country', 'city'), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('metadata', 'created_at'), 'classes': ('collapse',) }), ) readonly_fields = ['created_at'] def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related( 'survey_instance', 'survey_instance__patient', 'survey_instance__survey_template' ) def survey_instance_link(self, obj): """Link to survey instance""" url = f"/admin/surveys/surveyinstance/{obj.survey_instance.id}/change/" return format_html('{} - {}', url, obj.survey_instance.survey_template.name, obj.survey_instance.patient.get_full_name()) survey_instance_link.short_description = 'Survey' def event_type_badge(self, obj): """Display event type with color badge""" colors = { 'page_view': 'info', 'survey_started': 'primary', 'question_answered': 'secondary', 'survey_completed': 'success', 'survey_abandoned': 'danger', 'reminder_sent': 'warning', } color = colors.get(obj.event_type, 'secondary') return format_html( '{}', color, obj.get_event_type_display() ) event_type_badge.short_description = 'Event Type' def total_time_spent_display(self, obj): """Display time spent in human-readable format""" if obj.total_time_spent: minutes = obj.total_time_spent // 60 seconds = obj.total_time_spent % 60 return f"{minutes}m {seconds}s" return '-' total_time_spent_display.short_description = 'Time Spent' @admin.register(SurveyResponse) class SurveyResponseAdmin(admin.ModelAdmin): """Survey response admin""" list_display = [ 'survey_instance', 'question_preview', 'numeric_value', 'text_value_preview', 'created_at' ] list_filter = ['survey_instance__survey_template', 'question__question_type', 'created_at'] search_fields = [ 'survey_instance__patient__mrn', 'question__text', 'text_value' ] ordering = ['survey_instance', 'question__order'] fieldsets = ( (None, { 'fields': ('survey_instance', 'question') }), ('Response', { 'fields': ('numeric_value', 'text_value', 'choice_value') }), ('Metadata', { 'fields': ('created_at', 'updated_at') }), ) readonly_fields = ['created_at', 'updated_at'] def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('survey_instance', 'question') def question_preview(self, obj): """Show preview of question""" return obj.question.text[:50] + '...' if len(obj.question.text) > 50 else obj.question.text question_preview.short_description = 'Question' def text_value_preview(self, obj): """Show preview of text response""" if obj.text_value: return obj.text_value[:50] + '...' if len(obj.text_value) > 50 else obj.text_value return '-' text_value_preview.short_description = 'Text Response'