""" Admin configuration for laboratory app. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from django.utils import timezone from .models import ( LabTest, LabOrder, Specimen, LabResult, QualityControl, ReferenceRange ) @admin.register(LabTest) class LabTestAdmin(admin.ModelAdmin): """ Admin interface for LabTest model. """ list_display = [ 'test_code', 'test_name', 'test_category', 'department', 'specimen_type', 'turnaround_time', 'is_active' ] list_filter = [ 'test_category', 'test_type', 'department', 'specimen_type', 'is_active', 'is_orderable', 'stat_available', 'fasting_required' ] search_fields = [ 'test_code', 'test_name', 'loinc_code', 'cpt_code', 'snomed_code' ] readonly_fields = ['test_id', 'created_at', 'updated_at'] fieldsets = ( ('Basic Information', { 'fields': ( 'test_id', 'test_code', 'test_name', 'test_description' ) }), ('Medical Coding', { 'fields': ( 'loinc_code', 'cpt_code', 'snomed_code' ) }), ('Classification', { 'fields': ( 'test_category', 'test_type', 'department' ) }), ('Specimen Requirements', { 'fields': ( 'specimen_type', 'specimen_volume', 'collection_container', 'collection_instructions' ) }), ('Processing', { 'fields': ( 'processing_time', 'turnaround_time', 'stat_available', 'stat_turnaround_time' ) }), ('Storage and Transport', { 'fields': ( 'storage_temperature', 'transport_requirements', 'stability_time' ) }), ('Clinical Information', { 'fields': ( 'clinical_significance', 'indications', 'contraindications' ) }), ('Patient Preparation', { 'fields': ( 'patient_preparation', 'fasting_required', 'fasting_hours' ) }), ('Methodology', { 'fields': ( 'methodology', 'analyzer' ) }), ('Quality Control', { 'fields': ( 'qc_frequency', ) }), ('Pricing and Availability', { 'fields': ( 'cost', 'is_active', 'is_orderable' ) }), ('Metadata', { 'fields': ( 'created_at', 'updated_at', 'created_by' ) }), ) def get_queryset(self, request): """Filter by tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): return qs.filter(tenant=request.user.tenant) return qs.none() def save_model(self, request, obj, form, change): """Set tenant and created_by.""" if not change: obj.tenant = request.user.tenant obj.created_by = request.user super().save_model(request, obj, form, change) class SpecimenInline(admin.TabularInline): """ Inline admin for specimens. """ model = Specimen extra = 0 readonly_fields = ['specimen_id', 'specimen_number', 'created_at'] fields = [ 'specimen_type', 'container_type', 'volume', 'collected_datetime', 'collected_by', 'quality', 'status' ] class LabResultInline(admin.TabularInline): """ Inline admin for lab results. """ model = LabResult extra = 0 readonly_fields = ['result_id', 'created_at'] fields = [ 'test', 'specimen', 'result_value', 'result_unit', 'abnormal_flag', 'is_critical', 'status' ] @admin.register(LabOrder) class LabOrderAdmin(admin.ModelAdmin): """ Admin interface for LabOrder model. """ list_display = [ 'order_number', 'patient_name', 'ordering_provider', 'priority', 'status', 'order_datetime', 'test_count' ] list_filter = [ 'status', 'priority', 'order_datetime', 'fasting_status' ] search_fields = [ 'order_number', 'patient__first_name', 'patient__last_name', 'patient__mrn', 'ordering_provider__first_name', 'ordering_provider__last_name' ] readonly_fields = ['order_id', 'order_number', 'created_at', 'updated_at'] inlines = [SpecimenInline, LabResultInline] filter_horizontal = ['tests'] fieldsets = ( ('Order Information', { 'fields': ( 'order_id', 'order_number', 'patient', 'ordering_provider' ) }), ('Tests', { 'fields': ( 'tests', ) }), ('Order Details', { 'fields': ( 'order_datetime', 'priority' ) }), ('Clinical Information', { 'fields': ( 'clinical_indication', 'diagnosis_code', 'clinical_notes' ) }), ('Collection Information', { 'fields': ( 'collection_datetime', 'collection_location', 'fasting_status' ) }), ('Status', { 'fields': ( 'status', ) }), ('Related Information', { 'fields': ( 'encounter', ) }), ('Special Instructions', { 'fields': ( 'special_instructions', ) }), ('Metadata', { 'fields': ( 'created_at', 'updated_at' ) }), ) def patient_name(self, obj): """Get patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' def get_queryset(self, request): """Filter by tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): return qs.filter(tenant=request.user.tenant) return qs.none() def save_model(self, request, obj, form, change): """Set tenant.""" if not change: obj.tenant = request.user.tenant super().save_model(request, obj, form, change) @admin.register(Specimen) class SpecimenAdmin(admin.ModelAdmin): """ Admin interface for Specimen model. """ list_display = [ 'specimen_number', 'patient_name', 'specimen_type', 'collected_datetime', 'quality', 'status' ] list_filter = [ 'specimen_type', 'quality', 'status', 'collected_datetime', 'storage_temperature' ] search_fields = [ 'specimen_number', 'order__patient__first_name', 'order__patient__last_name', 'order__order_number' ] readonly_fields = [ 'specimen_id', 'specimen_number', 'created_at', 'updated_at' ] fieldsets = ( ('Specimen Information', { 'fields': ( 'specimen_id', 'specimen_number', 'order' ) }), ('Specimen Details', { 'fields': ( 'specimen_type', 'container_type', 'volume' ) }), ('Collection Information', { 'fields': ( 'collected_datetime', 'collected_by', 'collection_site', 'collection_method' ) }), ('Specimen Quality', { 'fields': ( 'quality', 'rejection_reason', 'quality_notes' ) }), ('Processing Information', { 'fields': ( 'received_datetime', 'received_by' ) }), ('Storage Information', { 'fields': ( 'storage_location', 'storage_temperature' ) }), ('Status', { 'fields': ( 'status', ) }), ('Chain of Custody', { 'fields': ( 'chain_of_custody', ) }), ('Metadata', { 'fields': ( 'created_at', 'updated_at' ) }), ) def patient_name(self, obj): """Get patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' @admin.register(LabResult) class LabResultAdmin(admin.ModelAdmin): """ Admin interface for LabResult model. """ list_display = [ 'patient_name', 'test_name', 'result_value', 'result_unit', 'abnormal_flag', 'is_critical', 'status', 'verified' ] list_filter = [ 'status', 'abnormal_flag', 'is_critical', 'verified', 'analyzed_datetime', 'test__test_category' ] search_fields = [ 'order__patient__first_name', 'order__patient__last_name', 'test__test_name', 'test__test_code', 'specimen__specimen_number' ] readonly_fields = [ 'result_id', 'created_at', 'updated_at' ] fieldsets = ( ('Result Information', { 'fields': ( 'result_id', 'order', 'test', 'specimen' ) }), ('Result Values', { 'fields': ( 'result_value', 'result_unit', 'result_type' ) }), ('Reference Range', { 'fields': ( 'reference_range', 'abnormal_flag' ) }), ('Critical Values', { 'fields': ( 'is_critical', 'critical_called', 'critical_called_datetime', 'critical_called_to' ) }), ('Processing Information', { 'fields': ( 'analyzed_datetime', 'analyzed_by', 'analyzer' ) }), ('Verification', { 'fields': ( 'verified', 'verified_by', 'verified_datetime' ) }), ('Status', { 'fields': ( 'status', ) }), ('Comments', { 'fields': ( 'technician_comments', 'pathologist_comments' ) }), ('Quality Control', { 'fields': ( 'qc_passed', 'qc_notes' ) }), ('Reporting', { 'fields': ( 'reported_datetime', ) }), ('Metadata', { 'fields': ( 'created_at', 'updated_at' ) }), ) def patient_name(self, obj): """Get patient name.""" return obj.patient.get_full_name() patient_name.short_description = 'Patient' def test_name(self, obj): """Get test name.""" return obj.test.test_name test_name.short_description = 'Test' @admin.register(QualityControl) class QualityControlAdmin(admin.ModelAdmin): """ Admin interface for QualityControl model. """ list_display = [ 'test_name', 'control_level', 'run_datetime', 'target_value', 'observed_value', 'status' ] list_filter = [ 'status', 'control_level', 'run_datetime', 'test__test_category' ] search_fields = [ 'test__test_name', 'test__test_code', 'control_material' ] readonly_fields = [ 'qc_id', 'created_at', 'updated_at' ] fieldsets = ( ('QC Information', { 'fields': ( 'qc_id', 'test' ) }), ('QC Material', { 'fields': ( 'control_material', 'control_lot', 'control_level' ) }), ('Expected Values', { 'fields': ( 'target_value', 'acceptable_range_low', 'acceptable_range_high' ) }), ('QC Run Information', { 'fields': ( 'run_datetime', 'observed_value' ) }), ('QC Status', { 'fields': ( 'status', ) }), ('Staff', { 'fields': ( 'performed_by', 'reviewed_by' ) }), ('Analyzer', { 'fields': ( 'analyzer', ) }), ('Comments', { 'fields': ( 'comments', 'corrective_action' ) }), ('Metadata', { 'fields': ( 'created_at', 'updated_at' ) }), ) def test_name(self, obj): """Get test name.""" return obj.test.test_name test_name.short_description = 'Test' def get_queryset(self, request): """Filter by tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): return qs.filter(tenant=request.user.tenant) return qs.none() def save_model(self, request, obj, form, change): """Set tenant.""" if not change: obj.tenant = request.user.tenant super().save_model(request, obj, form, change) @admin.register(ReferenceRange) class ReferenceRangeAdmin(admin.ModelAdmin): """ Admin interface for ReferenceRange model. """ list_display = [ 'test_name', 'gender', 'age_range', 'range_display', 'unit', 'is_active' ] list_filter = [ 'gender', 'is_active', 'test__test_category' ] search_fields = [ 'test__test_name', 'test__test_code' ] readonly_fields = [ 'range_id', 'created_at', 'updated_at' ] fieldsets = ( ('Range Information', { 'fields': ( 'range_id', 'test' ) }), ('Demographics', { 'fields': ( 'gender', 'age_min', 'age_max' ) }), ('Range Values', { 'fields': ( 'range_low', 'range_high', 'range_text' ) }), ('Critical Values', { 'fields': ( 'critical_low', 'critical_high' ) }), ('Units', { 'fields': ( 'unit', ) }), ('Status', { 'fields': ( 'is_active', ) }), ('Metadata', { 'fields': ( 'created_at', 'updated_at', 'created_by' ) }), ) def test_name(self, obj): """Get test name.""" return obj.test.test_name test_name.short_description = 'Test' def age_range(self, obj): """Get age range display.""" if obj.age_min is not None or obj.age_max is not None: return f"{obj.age_min or 0}-{obj.age_max or '∞'} years" return "All ages" age_range.short_description = 'Age Range' def range_display(self, obj): """Get range display.""" return obj.display_range range_display.short_description = 'Range' def save_model(self, request, obj, form, change): """Set created_by.""" if not change: obj.created_by = request.user super().save_model(request, obj, form, change)