Marwan Alwali 43901b5bda update
2025-09-09 01:15:48 +03:00

553 lines
17 KiB
Python

"""
Admin configuration for patients app.
"""
from django.contrib import admin
from django.utils.html import format_html
from .models import *
class EmergencyContactInline(admin.TabularInline):
"""
Inline admin for emergency contacts.
"""
model = EmergencyContact
extra = 1
fields = [
'first_name', 'last_name', 'relationship', 'phone_number',
'priority', 'is_authorized_for_medical_decisions', 'is_active'
]
class InsuranceInfoInline(admin.TabularInline):
"""
Inline admin for insurance information.
"""
model = InsuranceInfo
extra = 1
fields = [
'insurance_type', 'insurance_company', 'policy_number',
'effective_date', 'is_verified', 'is_active'
]
class ConsentFormInline(admin.TabularInline):
"""
Inline admin for consent forms.
"""
model = ConsentForm
extra = 0
fields = ['template', 'status', 'effective_date', 'patient_signed_at']
readonly_fields = ['patient_signed_at']
class PatientNoteInline(admin.TabularInline):
"""
Inline admin for patient notes.
"""
model = PatientNote
extra = 0
fields = ['title', 'category', 'priority', 'is_alert', 'created_at']
readonly_fields = ['created_at']
@admin.register(PatientProfile)
class PatientProfileAdmin(admin.ModelAdmin):
"""
Admin configuration for PatientProfile model.
"""
list_display = [
'mrn', 'get_full_name', 'date_of_birth', 'age', 'gender',
'mobile_number', 'is_active',
]
list_filter = [
'tenant', 'gender', 'marital_status',
'is_active', 'is_deceased', 'is_vip', 'confidential_patient',
]
search_fields = [
'mrn', 'id_number', 'first_name', 'last_name', 'email', 'mobile_number',
]
ordering = ['last_name', 'first_name']
fieldsets = (
('Basic Information', {
'fields': (
'tenant', 'mrn', 'first_name', 'last_name', 'middle_name',
'preferred_name', 'suffix', 'photo'
)
}),
('Demographics', {
'fields': (
'date_of_birth', 'gender', 'marital_status'
)
}),
('Contact Information', {
'fields': (
'email', 'phone_number', 'mobile_number',
'address_line_1', 'address_line_2', 'city', 'state',
'zip_code', 'country'
)
}),
('Language and Communication', {
'fields': (
'primary_language', 'interpreter_needed', 'communication_preference'
)
}),
('Employment', {
'fields': ('employer', 'occupation')
}),
('Healthcare Information', {
'fields': (
'primary_care_physician', 'referring_physician',
'allergies', 'medical_alerts'
)
}),
('Advance Directives', {
'fields': ('has_advance_directive', 'advance_directive_type')
}),
('Status and Flags', {
'fields': (
'is_active', 'is_deceased', 'date_of_death',
'is_vip', 'confidential_patient'
)
}),
('Registration', {
'fields': ( 'registered_by', 'last_visit_date'),
'classes': ('collapse',)
}),
('Metadata', {
'fields': ('patient_id', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
readonly_fields = ['patient_id', 'created_at', 'updated_at', 'age']
inlines = [EmergencyContactInline, InsuranceInfoInline, ConsentFormInline, PatientNoteInline]
def get_queryset(self, request):
return super().get_queryset(request).select_related('tenant', 'registered_by')
def age(self, obj):
return obj.age
age.short_description = 'Age'
@admin.register(EmergencyContact)
class EmergencyContactAdmin(admin.ModelAdmin):
"""
Admin configuration for EmergencyContact model.
"""
list_display = [
'get_full_name', 'patient', 'relationship', 'phone_number',
'priority', 'is_authorized_for_medical_decisions', 'is_active'
]
list_filter = [
'relationship', 'priority', 'is_authorized_for_medical_decisions',
'is_authorized_for_financial_decisions', 'is_authorized_for_information',
'is_active'
]
search_fields = [
'first_name', 'last_name', 'phone_number', 'email',
'patient__first_name', 'patient__last_name', 'patient__mrn'
]
ordering = ['patient__last_name', 'priority', 'last_name']
fieldsets = (
('Contact Information', {
'fields': (
'patient', 'first_name', 'last_name', 'relationship'
)
}),
('Contact Details', {
'fields': (
'phone_number', 'mobile_number', 'email'
)
}),
('Address', {
'fields': (
'address_line_1', 'address_line_2', 'city', 'state', 'zip_code'
)
}),
('Authorization', {
'fields': (
'priority', 'is_authorized_for_medical_decisions',
'is_authorized_for_financial_decisions', 'is_authorized_for_information'
)
}),
('Status', {
'fields': ('is_active', 'notes')
}),
('Metadata', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
readonly_fields = ['created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related('patient')
@admin.register(InsuranceInfo)
class InsuranceInfoAdmin(admin.ModelAdmin):
"""
Admin configuration for InsuranceInfo model.
"""
list_display = [
'patient', 'insurance_company', 'insurance_type', 'policy_number',
'is_verified', 'is_coverage_active', 'effective_date'
]
list_filter = [
'insurance_type', 'plan_type', 'is_verified', 'is_active',
'subscriber_relationship', 'effective_date'
]
search_fields = [
'insurance_company', 'plan_name', 'policy_number', 'group_number',
'subscriber_name', 'patient__first_name', 'patient__last_name', 'patient__mrn'
]
ordering = ['patient__last_name', 'insurance_type']
fieldsets = (
('Patient Information', {
'fields': ('patient',)
}),
('Insurance Details', {
'fields': (
'insurance_type', 'insurance_company', 'plan_name', 'plan_type'
)
}),
('Policy Information', {
'fields': ('policy_number', 'group_number')
}),
('Subscriber Information', {
'fields': (
'subscriber_name', 'subscriber_relationship',
'subscriber_dob', 'subscriber_ssn'
)
}),
('Coverage', {
'fields': ('effective_date', 'termination_date')
}),
('Financial Information', {
'fields': ('copay_amount', 'deductible_amount', 'out_of_pocket_max')
}),
('Verification', {
'fields': ('is_verified', 'verification_date', 'verified_by')
}),
('Authorization', {
'fields': (
'requires_authorization', 'authorization_number', 'authorization_expiry'
)
}),
('Status', {
'fields': ('is_active', 'notes')
}),
('Metadata', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
readonly_fields = ['created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related('patient', 'verified_by')
def is_coverage_active(self, obj):
return obj.is_coverage_active
is_coverage_active.boolean = True
is_coverage_active.short_description = 'Coverage Active'
@admin.register(ConsentTemplate)
class ConsentTemplateAdmin(admin.ModelAdmin):
"""
Admin configuration for ConsentTemplate model.
"""
list_display = [
'name', 'category', 'version', 'is_active',
'effective_date', 'created_by'
]
list_filter = [
'tenant', 'category', 'is_active', 'requires_signature',
'requires_witness', 'requires_guardian', 'effective_date'
]
search_fields = ['name', 'description', 'category']
ordering = ['category', 'name']
fieldsets = (
('Template Information', {
'fields': ('tenant', 'name', 'description', 'category')
}),
('Content', {
'fields': ('content',)
}),
('Requirements', {
'fields': (
'requires_signature', 'requires_witness', 'requires_guardian'
)
}),
('Validity', {
'fields': ('is_active', 'version', 'effective_date', 'expiry_date')
}),
('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')
@admin.register(ConsentForm)
class ConsentFormAdmin(admin.ModelAdmin):
"""
Admin configuration for ConsentForm model.
"""
list_display = [
'patient', 'template', 'status', 'is_valid',
'patient_signed_at', 'effective_date'
]
list_filter = [
'status', 'template__category', 'patient_signed_at',
'effective_date', 'created_at'
]
search_fields = [
'patient__first_name', 'patient__last_name', 'patient__mrn',
'template__name', 'guardian_name', 'witness_name', 'provider_name'
]
ordering = ['-created_at']
fieldsets = (
('Consent Information', {
'fields': ('patient', 'template', 'status')
}),
('Patient Signature', {
'fields': (
'patient_signature', 'patient_signed_at', 'patient_ip_address'
)
}),
('Guardian Signature', {
'fields': (
'guardian_signature', 'guardian_signed_at',
'guardian_name', 'guardian_relationship'
)
}),
('Witness Signature', {
'fields': (
'witness_signature', 'witness_signed_at',
'witness_name', 'witness_title'
)
}),
('Provider Information', {
'fields': (
'provider_name', 'provider_signature', 'provider_signed_at'
)
}),
('Validity', {
'fields': ('effective_date', 'expiry_date')
}),
('Revocation', {
'fields': ('revoked_at', 'revoked_by', 'revocation_reason')
}),
('Notes', {
'fields': ('notes',)
}),
('Metadata', {
'fields': ('consent_id', 'created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['consent_id', 'created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related(
'patient', 'template', 'created_by', 'revoked_by'
)
def is_valid(self, obj):
return obj.is_valid
is_valid.boolean = True
is_valid.short_description = 'Valid'
@admin.register(PatientNote)
class PatientNoteAdmin(admin.ModelAdmin):
"""
Admin configuration for PatientNote model.
"""
list_display = [
'title', 'patient', 'category', 'priority',
'is_alert', 'is_confidential', 'created_by', 'created_at'
]
list_filter = [
'category', 'priority', 'is_alert', 'is_confidential',
'is_active', 'created_at'
]
search_fields = [
'title', 'content', 'patient__first_name',
'patient__last_name', 'patient__mrn'
]
ordering = ['-created_at']
fieldsets = (
('Note Information', {
'fields': ('patient', 'title', 'content')
}),
('Classification', {
'fields': ('category', 'priority')
}),
('Visibility', {
'fields': ('is_confidential', 'is_alert', 'is_active')
}),
('Metadata', {
'fields': ('note_id', 'created_at', 'updated_at', 'created_by'),
'classes': ('collapse',)
}),
)
readonly_fields = ['note_id', 'created_at', 'updated_at']
def get_queryset(self, request):
return super().get_queryset(request).select_related('patient', 'created_by')
class ClaimDocumentInline(admin.TabularInline):
"""Inline admin for claim documents."""
model = ClaimDocument
extra = 0
readonly_fields = ['uploaded_at', 'file_size']
class ClaimStatusHistoryInline(admin.TabularInline):
"""Inline admin for claim status history."""
model = ClaimStatusHistory
extra = 0
readonly_fields = ['changed_at']
ordering = ['-changed_at']
fields = [ 'changed_at']
@admin.register(InsuranceClaim)
class InsuranceClaimAdmin(admin.ModelAdmin):
"""Admin interface for InsuranceClaim."""
list_display = [
'claim_number', 'patient', 'claim_type', 'priority',
'billed_amount', 'approved_amount', 'service_date', 'submitted_date'
]
list_filter = [
'claim_type', 'priority', 'service_date',
'submitted_date', 'processed_date'
]
search_fields = [
'claim_number', 'patient__first_name', 'patient__last_name',
'service_provider', 'facility_name', 'primary_diagnosis_code'
]
ordering = ['-created_at']
fieldsets = (
('Claim Information', {
'fields': ('claim_number', 'patient', 'insurance_info', 'claim_type', 'status', 'priority')
}),
('Service Information', {
'fields': (
'service_date', 'service_provider', 'service_provider_license',
'facility_name', 'facility_license'
)
}),
('Medical Codes', {
'fields': (
'primary_diagnosis_code', 'primary_diagnosis_description',
'secondary_diagnosis_codes', 'procedure_codes'
)
}),
('Financial Information', {
'fields': (
'billed_amount', 'approved_amount', 'paid_amount',
'patient_responsibility', 'discount_amount'
)
}),
('Processing Dates', {
'fields': ('submitted_date', 'processed_date', 'payment_date')
}),
('Saudi-specific Information', {
'fields': ('saudi_id_number', 'insurance_card_number', 'authorization_number')
}),
('Denial/Appeal Information', {
'fields': ('denial_reason', 'denial_code', 'appeal_date', 'appeal_reason'),
'classes': ('collapse',)
}),
('Additional Information', {
'fields': ('notes', 'attachments'),
'classes': ('collapse',)
})
)
readonly_fields = ['created_at', 'updated_at']
inlines = [ClaimDocumentInline, ClaimStatusHistoryInline]
def get_queryset(self, request):
"""Optimize queryset with select_related."""
return super().get_queryset(request).select_related('patient', 'insurance_info')
def formfield_for_foreignkey(self, db_field, request, **kwargs):
"""Optimize foreign key fields."""
if db_field.name == "patient":
kwargs["queryset"] = PatientProfile.objects.select_related('tenant')
elif db_field.name == "insurance_info":
kwargs["queryset"] = InsuranceInfo.objects.select_related('patient')
return super().formfield_for_foreignkey(db_field, request, **kwargs)
@admin.register(ClaimDocument)
class ClaimDocumentAdmin(admin.ModelAdmin):
"""Admin interface for ClaimDocument."""
list_display = [
'title', 'claim', 'document_type', 'file_size_display',
'mime_type', 'uploaded_at', 'uploaded_by'
]
list_filter = ['document_type', 'mime_type', 'uploaded_at']
search_fields = ['title', 'claim__claim_number', 'description']
ordering = ['-uploaded_at']
def file_size_display(self, obj):
"""Display file size in human readable format."""
size = obj.file_size
for unit in ['B', 'KB', 'MB', 'GB']:
if size < 1024.0:
return f"{size:.1f} {unit}"
size /= 1024.0
return f"{size:.1f} TB"
file_size_display.short_description = 'File Size'
@admin.register(ClaimStatusHistory)
class ClaimStatusHistoryAdmin(admin.ModelAdmin):
"""Admin interface for ClaimStatusHistory."""
list_display = [
'claim', 'from_status', 'to_status', 'changed_at', 'changed_by'
]
list_filter = ['from_status', 'to_status', 'changed_at']
search_fields = ['claim__claim_number', 'reason', 'notes']
ordering = ['-changed_at']
def get_queryset(self, request):
"""Optimize queryset with select_related."""
return super().get_queryset(request).select_related('claim', 'changed_by')
# Custom admin site configuration
admin.site.site_header = "Hospital Management System - Patients"
admin.site.site_title = "HMS Patients Admin"
admin.site.index_title = "Patients Administration"