235 lines
7.3 KiB
Python
235 lines
7.3 KiB
Python
"""
|
|
Surveys admin
|
|
"""
|
|
from django.contrib import admin
|
|
from django.utils.html import format_html
|
|
|
|
from .models import SurveyInstance, SurveyQuestion, SurveyResponse, SurveyTemplate
|
|
|
|
|
|
class SurveyQuestionInline(admin.TabularInline):
|
|
"""Inline admin for survey questions"""
|
|
model = SurveyQuestion
|
|
extra = 1
|
|
fields = ['order', 'text', 'question_type', 'is_required', 'weight']
|
|
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', 'version'
|
|
]
|
|
list_filter = ['survey_type', 'scoring_method', 'is_active', 'hospital']
|
|
search_fields = ['name', 'name_ar', 'description']
|
|
ordering = ['hospital', 'name']
|
|
inlines = [SurveyQuestionInline]
|
|
|
|
fieldsets = (
|
|
(None, {
|
|
'fields': ('name', 'name_ar', 'description', 'description_ar')
|
|
}),
|
|
('Configuration', {
|
|
'fields': ('hospital', 'survey_type', 'version')
|
|
}),
|
|
('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', 'weight'
|
|
]
|
|
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', 'weight')
|
|
}),
|
|
('Choices (for multiple choice)', {
|
|
'fields': ('choices_json',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Branch Logic', {
|
|
'fields': ('branch_logic',),
|
|
'classes': ('collapse',)
|
|
}),
|
|
('Help Text', {
|
|
'fields': ('help_text', 'help_text_ar'),
|
|
'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
|
|
|
|
|
|
@admin.register(SurveyInstance)
|
|
class SurveyInstanceAdmin(admin.ModelAdmin):
|
|
"""Survey instance admin"""
|
|
list_display = [
|
|
'survey_template', 'patient', 'encounter_id',
|
|
'status_badge', 'delivery_channel', '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]
|
|
|
|
fieldsets = (
|
|
(None, {
|
|
'fields': ('survey_template', 'patient', 'encounter_id')
|
|
}),
|
|
('Journey Linkage', {
|
|
'fields': ('journey_instance', 'journey_stage_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')
|
|
}),
|
|
('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', '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', 'journey_stage_instance'
|
|
).prefetch_related('responses')
|
|
|
|
def status_badge(self, obj):
|
|
"""Display status with color badge"""
|
|
colors = {
|
|
'pending': 'warning',
|
|
'active': 'info',
|
|
'completed': 'success',
|
|
'cancelled': 'secondary',
|
|
}
|
|
color = colors.get(obj.status, 'secondary')
|
|
return format_html(
|
|
'<span class="badge bg-{}">{}</span>',
|
|
color,
|
|
obj.get_status_display()
|
|
)
|
|
status_badge.short_description = 'Status'
|
|
|
|
|
|
@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': ('response_time_seconds', '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'
|