2025-08-12 13:33:25 +03:00

462 lines
14 KiB
Python

"""
Admin configuration for appointments app.
"""
from django.contrib import admin
from django.utils.html import format_html
from django.utils import timezone
from .models import (
AppointmentRequest, SlotAvailability, WaitingQueue, QueueEntry,
TelemedicineSession, AppointmentTemplate
)
class QueueEntryInline(admin.TabularInline):
"""
Inline admin for queue entries.
"""
model = QueueEntry
extra = 0
fields = [
'patient', 'queue_position', 'priority_score', 'status',
'assigned_provider', 'joined_at'
]
readonly_fields = ['joined_at']
class TelemedicineSessionInline(admin.StackedInline):
"""
Inline admin for telemedicine sessions.
"""
model = TelemedicineSession
extra = 0
fields = [
'platform', 'meeting_url', 'meeting_id', 'meeting_password',
'status', 'scheduled_start', 'scheduled_end'
]
@admin.register(AppointmentRequest)
class AppointmentRequestAdmin(admin.ModelAdmin):
"""
Admin configuration for AppointmentRequest model.
"""
list_display = [
'patient', 'provider', 'appointment_type', 'specialty',
'scheduled_datetime', 'status', 'priority', 'is_telemedicine'
]
list_filter = [
'tenant', 'appointment_type', 'specialty', 'status', 'priority',
'is_telemedicine', 'insurance_verified', 'authorization_required',
'scheduled_datetime', 'created_at'
]
search_fields = [
'patient__first_name', 'patient__last_name', 'patient__mrn',
'provider__first_name', 'provider__last_name',
'chief_complaint', 'referring_provider'
]
ordering = ['-scheduled_datetime', '-created_at']
fieldsets = (
('Basic Information', {
'fields': (
'tenant', 'patient', 'provider', 'appointment_type', 'specialty'
)
}),
('Scheduling', {
'fields': (
'preferred_date', 'preferred_time', 'duration_minutes',
'scheduled_datetime', 'scheduled_end_datetime',
'flexible_scheduling', 'earliest_acceptable_date', 'latest_acceptable_date'
)
}),
('Priority and Urgency', {
'fields': ('priority', 'urgency_score')
}),
('Clinical Information', {
'fields': (
'chief_complaint', 'clinical_notes', 'referring_provider'
)
}),
('Insurance and Authorization', {
'fields': (
'insurance_verified', 'authorization_required', 'authorization_number'
)
}),
('Location', {
'fields': ('location', 'room_number')
}),
('Telemedicine', {
'fields': (
'is_telemedicine', 'telemedicine_platform',
'meeting_url', 'meeting_id', 'meeting_password'
)
}),
('Status and Workflow', {
'fields': ('status',)
}),
('Check-in Information', {
'fields': ('checked_in_at', 'checked_in_by'),
'classes': ('collapse',)
}),
('Completion Information', {
'fields': ('completed_at', 'actual_duration_minutes'),
'classes': ('collapse',)
}),
('Cancellation Information', {
'fields': ('cancelled_at', 'cancelled_by', 'cancellation_reason'),
'classes': ('collapse',)
}),
('Special Requirements', {
'fields': (
'special_requirements', 'interpreter_needed', 'interpreter_language'
),
'classes': ('collapse',)
}),
('Metadata', {
'fields': ('request_id', 'created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['request_id', 'created_at', 'updated_at']
inlines = [TelemedicineSessionInline, QueueEntryInline]
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'tenant', 'patient', 'provider', 'created_by', 'checked_in_by', 'cancelled_by'
)
def is_telemedicine(self, obj):
return obj.is_telemedicine
is_telemedicine.boolean = True
is_telemedicine.short_description = 'Telemedicine'
@admin.register(SlotAvailability)
class SlotAvailabilityAdmin(admin.ModelAdmin):
"""
Admin configuration for SlotAvailability model.
"""
list_display = [
'provider', 'date', 'start_time', 'end_time', 'specialty',
'max_appointments', 'booked_appointments', 'available_capacity',
'is_active', 'is_blocked'
]
list_filter = [
'tenant', 'provider', 'specialty', 'availability_type',
'is_active', 'is_blocked', 'supports_telemedicine',
'telemedicine_only', 'date'
]
search_fields = [
'provider__first_name', 'provider__last_name',
'specialty', 'location', 'room_number'
]
ordering = ['date', 'start_time']
fieldsets = (
('Basic Information', {
'fields': ('tenant', 'provider', 'specialty')
}),
('Date and Time', {
'fields': (
'date', 'start_time', 'end_time', 'duration_minutes'
)
}),
('Capacity', {
'fields': ('max_appointments', 'booked_appointments')
}),
('Location', {
'fields': ('location', 'room_number')
}),
('Availability Type', {
'fields': ('availability_type',)
}),
('Telemedicine', {
'fields': ('supports_telemedicine', 'telemedicine_only')
}),
('Status', {
'fields': ('is_active', 'is_blocked', 'block_reason')
}),
('Recurring Pattern', {
'fields': (
'is_recurring', 'recurrence_pattern', 'recurrence_end_date'
),
'classes': ('collapse',)
}),
('Restrictions', {
'fields': ('patient_restrictions', 'insurance_restrictions'),
'classes': ('collapse',)
}),
('Metadata', {
'fields': ('slot_id', 'created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['slot_id', 'created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'tenant', 'provider', 'created_by'
)
def available_capacity(self, obj):
return obj.available_capacity
available_capacity.short_description = 'Available'
@admin.register(WaitingQueue)
class WaitingQueueAdmin(admin.ModelAdmin):
"""
Admin configuration for WaitingQueue model.
"""
list_display = [
'name', 'queue_type', 'specialty', 'current_queue_size',
'max_queue_size', 'estimated_wait_time_minutes',
'is_active', 'is_accepting_patients'
]
list_filter = [
'tenant', 'queue_type', 'specialty', 'is_active', 'is_accepting_patients'
]
search_fields = ['name', 'description', 'specialty', 'location']
ordering = ['name']
fieldsets = (
('Basic Information', {
'fields': ('tenant', 'name', 'description', 'queue_type')
}),
('Associated Resources', {
'fields': ('providers', 'specialty', 'location')
}),
('Queue Management', {
'fields': (
'max_queue_size', 'average_service_time_minutes', 'priority_weights'
)
}),
('Status', {
'fields': ('is_active', 'is_accepting_patients')
}),
('Operating Hours', {
'fields': ('operating_hours',),
'classes': ('collapse',)
}),
('Metadata', {
'fields': ('queue_id', 'created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['queue_id', 'created_at', 'updated_at']
inlines = [QueueEntryInline]
filter_horizontal = ['providers']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'tenant', 'created_by'
).prefetch_related('providers')
def current_queue_size(self, obj):
return obj.current_queue_size
current_queue_size.short_description = 'Current Size'
def estimated_wait_time_minutes(self, obj):
return f"{obj.estimated_wait_time_minutes} min"
estimated_wait_time_minutes.short_description = 'Est. Wait Time'
@admin.register(QueueEntry)
class QueueEntryAdmin(admin.ModelAdmin):
"""
Admin configuration for QueueEntry model.
"""
list_display = [
'patient', 'queue', 'queue_position', 'priority_score',
'status', 'wait_time_display', 'assigned_provider', 'joined_at'
]
list_filter = [
'queue', 'status', 'assigned_provider', 'notification_sent',
'notification_method', 'joined_at'
]
search_fields = [
'patient__first_name', 'patient__last_name', 'patient__mrn',
'queue__name', 'assigned_provider__first_name', 'assigned_provider__last_name'
]
ordering = ['queue', 'priority_score', 'joined_at']
fieldsets = (
('Queue Information', {
'fields': ('queue', 'patient', 'appointment')
}),
('Position and Priority', {
'fields': ('queue_position', 'priority_score')
}),
('Timing', {
'fields': (
'joined_at', 'estimated_service_time',
'called_at', 'served_at'
)
}),
('Status and Assignment', {
'fields': ('status', 'assigned_provider')
}),
('Communication', {
'fields': ('notification_sent', 'notification_method')
}),
('Notes', {
'fields': ('notes',)
}),
('Metadata', {
'fields': ('entry_id', 'updated_at', 'updated_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['entry_id', 'joined_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'queue', 'patient', 'appointment', 'assigned_provider', 'updated_by'
)
def wait_time_display(self, obj):
wait_time = obj.wait_time_minutes
if wait_time is not None:
return f"{wait_time} min"
return "-"
wait_time_display.short_description = 'Wait Time'
@admin.register(TelemedicineSession)
class TelemedicineSessionAdmin(admin.ModelAdmin):
"""
Admin configuration for TelemedicineSession model.
"""
list_display = [
'appointment', 'platform', 'status', 'scheduled_start',
'duration_display', 'connection_quality', 'recording_enabled'
]
list_filter = [
'platform', 'status', 'connection_quality', 'recording_enabled',
'recording_consent', 'encryption_enabled', 'scheduled_start'
]
search_fields = [
'appointment__patient__first_name', 'appointment__patient__last_name',
'appointment__provider__first_name', 'appointment__provider__last_name',
'meeting_id'
]
ordering = ['-scheduled_start']
fieldsets = (
('Session Information', {
'fields': ('appointment', 'platform')
}),
('Meeting Details', {
'fields': (
'meeting_url', 'meeting_id', 'meeting_password'
)
}),
('Configuration', {
'fields': (
'waiting_room_enabled', 'recording_enabled', 'recording_consent'
)
}),
('Security', {
'fields': ('encryption_enabled', 'password_required')
}),
('Timing', {
'fields': (
'scheduled_start', 'scheduled_end',
'actual_start', 'actual_end'
)
}),
('Participants', {
'fields': ('provider_joined_at', 'patient_joined_at')
}),
('Technical Information', {
'fields': ('connection_quality', 'technical_issues')
}),
('Recording', {
'fields': (
'recording_url', 'recording_duration_minutes'
),
'classes': ('collapse',)
}),
('Notes', {
'fields': ('session_notes',)
}),
('Metadata', {
'fields': ('session_id', 'created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['session_id', 'created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'appointment__patient', 'appointment__provider', 'created_by'
)
def duration_display(self, obj):
duration = obj.duration_minutes
if duration is not None:
return f"{duration} min"
return "-"
duration_display.short_description = 'Duration'
@admin.register(AppointmentTemplate)
class AppointmentTemplateAdmin(admin.ModelAdmin):
"""
Admin configuration for AppointmentTemplate model.
"""
list_display = [
'name', 'specialty', 'appointment_type', 'duration_minutes',
'advance_booking_days', 'minimum_notice_hours', 'is_active'
]
list_filter = [
'tenant', 'specialty', 'appointment_type', 'is_active',
'insurance_verification_required', 'authorization_required'
]
search_fields = ['name', 'description', 'specialty', 'appointment_type']
ordering = ['specialty', 'name']
fieldsets = (
('Template Information', {
'fields': ('tenant', 'name', 'description')
}),
('Appointment Configuration', {
'fields': ('appointment_type', 'specialty', 'duration_minutes')
}),
('Scheduling Rules', {
'fields': ('advance_booking_days', 'minimum_notice_hours')
}),
('Requirements', {
'fields': ('insurance_verification_required', 'authorization_required')
}),
('Instructions', {
'fields': (
'pre_appointment_instructions', 'post_appointment_instructions'
)
}),
('Forms and Documents', {
'fields': ('required_forms',)
}),
('Status', {
'fields': ('is_active',)
}),
('Metadata', {
'fields': ('created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'tenant', 'created_by'
)