""" Admin configuration for inpatients app. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from django.utils.safestring import mark_safe from .models import Ward, Bed, Admission, Transfer, DischargeSummary @admin.register(Ward) class WardAdmin(admin.ModelAdmin): """ Admin interface for Ward model. """ list_display = [ 'tenant','name', 'ward_id', 'ward_type', 'specialty', 'total_beds', 'occupancy_display', 'building', 'floor', 'is_active' ] list_filter = [ 'ward_type', 'specialty', 'building', 'floor', 'is_active', 'is_accepting_admissions', 'gender_restrictions' ] search_fields = ['name', 'ward_id', 'description', 'building'] readonly_fields = ['created_at', 'updated_at', 'occupancy_display'] filter_horizontal = ['attending_physicians'] fieldsets = ( ('Basic Information', { 'fields': ('tenant', 'ward_id', 'name', 'description', 'ward_type', 'specialty') }), ('Capacity', { 'fields': ('total_beds', 'private_rooms', 'semi_private_rooms', 'shared_rooms') }), ('Location', { 'fields': ('building', 'floor', 'wing') }), ('Staffing', { 'fields': ('nurse_manager', 'attending_physicians', 'min_nurses_day', 'min_nurses_night', 'nurse_to_patient_ratio') }), ('Equipment & Features', { 'fields': ('equipment_list', 'special_features'), 'classes': ('collapse',) }), ('Admission Criteria', { 'fields': ('admission_criteria', 'age_restrictions', 'gender_restrictions'), 'classes': ('collapse',) }), ('Status', { 'fields': ('is_active', 'is_accepting_admissions', 'closure_reason') }), ('Contact', { 'fields': ('phone_number', 'extension'), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('created_at', 'updated_at', 'created_by'), 'classes': ('collapse',) }) ) def occupancy_display(self, obj): """Display occupancy rate with color coding.""" rate = obj.occupancy_rate if rate >= 90: color = 'red' elif rate >= 75: color = 'orange' else: color = 'green' filled_beds = obj.total_beds - obj.available_beds return format_html( '{}% ({}/{})', color, rate, filled_beds, obj.total_beds ) occupancy_display.short_description = 'Occupancy' def save_model(self, request, obj, form, change): if not change: obj.created_by = request.user super().save_model(request, obj, form, change) class BedInline(admin.TabularInline): """ Inline admin for beds within ward. """ model = Bed extra = 0 fields = ['bed_number', 'room_number', 'bed_type', 'room_type', 'status',] @admin.register(Bed) class BedAdmin(admin.ModelAdmin): """ Admin interface for Bed model. """ list_display = [ 'bed_display', 'ward', 'bed_type', 'room_type', 'status_display', 'current_admission__patient', 'occupied_duration', 'last_cleaned' ] list_filter = [ 'ward', 'bed_type', 'room_type', 'status', 'cleaning_level', 'ward__building', 'ward__floor' ] search_fields = ['bed_number', 'room_number', 'ward__name', 'current_admission__patient__first_name', 'current_admission__patient__last_name'] readonly_fields = ['created_at', 'updated_at', 'occupancy_duration'] fieldsets = ( ('Basic Information', { 'fields': ('ward', 'bed_number', 'room_number', 'bed_type', 'room_type') }), ('Current Status', { 'fields': ('status', 'current_admission', 'occupied_since', 'reserved_until') }), ('Equipment & Features', { 'fields': ('equipment', 'features', 'bed_position'), 'classes': ('collapse',) }), ('Maintenance', { 'fields': ('last_maintenance', 'next_maintenance', 'maintenance_notes'), 'classes': ('collapse',) }), ('Cleaning', { 'fields': ('last_cleaned', 'cleaned_by', 'cleaning_level'), 'classes': ('collapse',) }), ('Blocking', { 'fields': ('blocked_reason', 'blocked_by', 'blocked_until'), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('created_at', 'updated_at', 'created_by'), 'classes': ('collapse',) }) ) def bed_display(self, obj): """Display bed with room information.""" return f"Room {obj.room_number}, Bed {obj.bed_number}" bed_display.short_description = 'Bed' def status_display(self, obj): """Display status with color coding.""" colors = { 'AVAILABLE': 'green', 'OCCUPIED': 'blue', 'RESERVED': 'orange', 'MAINTENANCE': 'red', 'CLEANING': 'purple', 'OUT_OF_ORDER': 'red', 'BLOCKED': 'gray' } color = colors.get(obj.status, 'black') return format_html( '{}', color, obj.get_status_display() ) status_display.short_description = 'Status' def occupied_duration(self, obj): """Display how long bed has been occupied.""" duration = obj.occupancy_duration if duration: days = duration.days hours = duration.seconds // 3600 return f"{days}d {hours}h" return "-" occupied_duration.short_description = 'Occupied For' def save_model(self, request, obj, form, change): if not change: obj.created_by = request.user super().save_model(request, obj, form, change) @admin.register(Admission) class AdmissionAdmin(admin.ModelAdmin): """ Admin interface for Admission model. """ list_display = [ 'patient', 'admission_datetime', 'admission_type', 'current_ward', 'current_bed', 'status_display', 'length_of_stay', 'attending_physician' ] list_filter = [ 'admission_type', 'admission_source', 'status', 'priority', 'acuity_level', 'current_ward', 'isolation_required', 'code_status', 'admission_datetime' ] search_fields = [ 'patient__first_name', 'patient__last_name', 'patient__mrn', 'chief_complaint', 'admitting_diagnosis', 'admission_id' ] readonly_fields = ['admission_id', 'created_at', 'updated_at', 'length_of_stay', 'is_active'] filter_horizontal = ['consulting_physicians'] date_hierarchy = 'admission_datetime' fieldsets = ( ('Patient Information', { 'fields': ('tenant', 'admission_id', 'patient') }), ('Admission Details', { 'fields': ('admission_datetime', 'admission_type', 'admission_source', 'chief_complaint', 'admitting_diagnosis', 'secondary_diagnoses') }), ('Providers', { 'fields': ('admitting_physician', 'attending_physician', 'consulting_physicians') }), ('Location', { 'fields': ('current_ward', 'current_bed') }), ('Status & Priority', { 'fields': ('status', 'priority', 'acuity_level') }), ('Insurance & Financial', { 'fields': ('insurance_verified', 'authorization_number', 'estimated_length_of_stay'), 'classes': ('collapse',) }), ('Discharge Planning', { 'fields': ('discharge_planning_started', 'discharge_planner', 'anticipated_discharge_date', 'discharge_datetime', 'discharge_disposition'), 'classes': ('collapse',) }), ('Special Requirements', { 'fields': ('isolation_required', 'isolation_type', 'special_needs', 'allergies', 'alerts'), 'classes': ('collapse',) }), ('Code Status & Directives', { 'fields': ('code_status', 'advance_directive', 'healthcare_proxy'), 'classes': ('collapse',) }), ('Notes', { 'fields': ('admission_notes',), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('created_at', 'updated_at', 'created_by', 'length_of_stay', 'is_active'), 'classes': ('collapse',) }) ) def status_display(self, obj): """Display status with color coding.""" colors = { 'PENDING': 'orange', 'ADMITTED': 'green', 'TRANSFERRED': 'blue', 'DISCHARGED': 'gray', 'DECEASED': 'red', 'LEFT_AMA': 'red', 'CANCELLED': 'red' } color = colors.get(obj.status, 'black') return format_html( '{}', color, obj.get_status_display() ) status_display.short_description = 'Status' def save_model(self, request, obj, form, change): if not change: obj.created_by = request.user super().save_model(request, obj, form, change) @admin.register(Transfer) class TransferAdmin(admin.ModelAdmin): """ Admin interface for Transfer model. """ list_display = [ 'patient', 'transfer_type', 'from_ward', 'to_ward', 'status_display', 'priority', 'requested_datetime', 'actual_datetime' ] list_filter = [ 'transfer_type', 'status', 'priority', 'patient_condition', 'from_ward', 'to_ward', 'transport_method', 'requested_datetime' ] search_fields = [ 'patient__first_name', 'patient__last_name', 'patient__mrn', 'reason', 'transfer_id' ] readonly_fields = ['transfer_id', 'created_at', 'updated_at', 'transfer_duration'] filter_horizontal = ['transport_team'] date_hierarchy = 'requested_datetime' fieldsets = ( ('Transfer Information', { 'fields': ('transfer_id', 'admission', 'patient', 'transfer_type') }), ('Location', { 'fields': ('from_ward', 'from_bed', 'to_ward', 'to_bed') }), ('Timing', { 'fields': ('requested_datetime', 'scheduled_datetime', 'actual_datetime') }), ('Details', { 'fields': ('reason', 'priority', 'status') }), ('Staff', { 'fields': ('requested_by', 'approved_by', 'completed_by') }), ('Transport', { 'fields': ('transport_method', 'transport_team', 'equipment_needed', 'supplies_needed'), 'classes': ('collapse',) }), ('Clinical', { 'fields': ('patient_condition', 'vital_signs', 'handoff_report', 'medications_transferred'), 'classes': ('collapse',) }), ('Issues', { 'fields': ('delay_reason', 'complications'), 'classes': ('collapse',) }), ('Notes', { 'fields': ('notes',), 'classes': ('collapse',) }), ('Metadata', { 'fields': ('created_at', 'updated_at', 'transfer_duration'), 'classes': ('collapse',) }) ) def status_display(self, obj): """Display status with color coding.""" colors = { 'REQUESTED': 'orange', 'APPROVED': 'blue', 'SCHEDULED': 'blue', 'IN_PROGRESS': 'purple', 'COMPLETED': 'green', 'CANCELLED': 'red', 'DELAYED': 'orange' } color = colors.get(obj.status, 'black') return format_html( '{}', color, obj.get_status_display() ) status_display.short_description = 'Status' @admin.register(DischargeSummary) class DischargeSummaryAdmin(admin.ModelAdmin): """ Admin interface for DischargeSummary model. """ list_display = [ 'patient_name', 'discharge_date', 'length_of_stay', 'discharge_disposition', 'discharging_physician', 'summary_completed', 'summary_signed' ] list_filter = [ 'discharge_disposition', 'readmission_risk', 'summary_completed', 'summary_signed', 'patient_copy_provided', 'discharge_date', 'transportation_method' ] search_fields = [ 'admission__patient__first_name', 'admission__patient__last_name', 'admission__patient__mrn', 'summary_id', 'final_diagnosis' ] readonly_fields = ['summary_id', 'created_at', 'updated_at'] date_hierarchy = 'discharge_date' fieldsets = ( ('Basic Information', { 'fields': ('summary_id', 'admission', 'discharge_date', 'discharge_time', 'length_of_stay') }), ('Clinical Summary', { 'fields': ('admission_diagnosis', 'final_diagnosis', 'secondary_diagnoses', 'procedures_performed', 'hospital_course', 'complications') }), ('Medications', { 'fields': ('discharge_medications', 'medication_changes'), 'classes': ('collapse',) }), ('Instructions', { 'fields': ('activity_restrictions', 'diet_instructions', 'wound_care', 'special_instructions'), 'classes': ('collapse',) }), ('Follow-up', { 'fields': ('follow_up_appointments', 'follow_up_instructions', 'warning_signs', 'when_to_call'), 'classes': ('collapse',) }), ('Discharge Details', { 'fields': ('discharge_disposition', 'discharge_location', 'transportation_arranged', 'transportation_method') }), ('Equipment & Supplies', { 'fields': ('durable_medical_equipment', 'supplies_provided'), 'classes': ('collapse',) }), ('Education', { 'fields': ('education_provided', 'education_materials', 'patient_understanding'), 'classes': ('collapse',) }), ('Planning', { 'fields': ('discharge_planner', 'social_worker_involved', 'case_manager_involved') }), ('Quality', { 'fields': ('readmission_risk', 'patient_satisfaction') }), ('Providers', { 'fields': ('discharging_physician', 'primary_nurse') }), ('Documentation', { 'fields': ('summary_completed', 'summary_signed', 'patient_copy_provided') }), ('Metadata', { 'fields': ('created_at', 'updated_at', 'created_by'), 'classes': ('collapse',) }) ) def patient_name(self, obj): """Display patient name.""" return obj.admission.patient.get_full_name() patient_name.short_description = 'Patient' def save_model(self, request, obj, form, change): if not change: obj.created_by = request.user super().save_model(request, obj, form, change) # Register Ward with Bed inline WardAdmin.inlines = [BedInline]