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

559 lines
15 KiB
Python

"""
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)