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 ( BloodGroup, Donor, BloodComponent, BloodUnit, BloodTest, CrossMatch, BloodRequest, BloodIssue, Transfusion, AdverseReaction, InventoryLocation, QualityControl ) @admin.register(BloodGroup) class BloodGroupAdmin(admin.ModelAdmin): list_display = ['abo_type', 'rh_factor', 'display_name'] list_filter = ['abo_type', 'rh_factor'] ordering = ['abo_type', 'rh_factor'] @admin.register(Donor) class DonorAdmin(admin.ModelAdmin): list_display = [ 'donor_id', 'full_name', 'blood_group', 'age', 'status', 'total_donations', 'last_donation_date', 'is_eligible_for_donation' ] list_filter = [ 'status', 'donor_type', 'blood_group', 'gender', 'registration_date', 'last_donation_date' ] search_fields = ['donor_id', 'first_name', 'last_name', 'phone', 'email'] readonly_fields = ['registration_date', 'created_at', 'updated_at', 'age'] fieldsets = ( ('Personal Information', { 'fields': ( 'donor_id', 'first_name', 'last_name', 'date_of_birth', 'gender', 'blood_group', 'weight', 'height' ) }), ('Contact Information', { 'fields': ( 'phone', 'email', 'address', 'emergency_contact_name', 'emergency_contact_phone' ) }), ('Donation Information', { 'fields': ( 'donor_type', 'status', 'total_donations', 'last_donation_date', 'notes' ) }), ('System Information', { 'fields': ('created_by', 'registration_date', 'created_at', 'updated_at'), 'classes': ['collapse'] }) ) def is_eligible_for_donation(self, obj): if obj.is_eligible_for_donation: return format_html('✓ Eligible') else: return format_html('✗ Not Eligible') is_eligible_for_donation.short_description = 'Eligible' @admin.register(BloodComponent) class BloodComponentAdmin(admin.ModelAdmin): list_display = ['name', 'shelf_life_days', 'storage_temperature', 'volume_ml', 'is_active'] list_filter = ['is_active', 'shelf_life_days'] search_fields = ['name', 'description'] class BloodTestInline(admin.TabularInline): model = BloodTest extra = 0 readonly_fields = ['test_date', 'verified_at'] class CrossMatchInline(admin.TabularInline): model = CrossMatch extra = 0 readonly_fields = ['test_date', 'verified_at'] @admin.register(BloodUnit) class BloodUnitAdmin(admin.ModelAdmin): list_display = [ 'unit_number', 'donor', 'component', 'blood_group', 'collection_date', 'expiry_date', 'status', 'days_to_expiry', 'is_available' ] list_filter = [ 'status', 'component', 'blood_group', 'collection_date', 'expiry_date', 'collection_site' ] search_fields = ['unit_number', 'donor__donor_id', 'donor__first_name', 'donor__last_name'] readonly_fields = ['created_at', 'updated_at', 'days_to_expiry', 'is_expired'] inlines = [BloodTestInline, CrossMatchInline] fieldsets = ( ('Unit Information', { 'fields': ( 'unit_number', 'donor', 'component', 'blood_group', 'volume_ml', 'status', 'location' ) }), ('Collection Details', { 'fields': ( 'collection_date', 'expiry_date', 'collection_site', 'bag_type', 'anticoagulant', 'collected_by' ) }), ('Additional Information', { 'fields': ('notes', 'created_at', 'updated_at'), 'classes': ['collapse'] }) ) def days_to_expiry(self, obj): days = obj.days_to_expiry if days == 0: return format_html('Expired') elif days <= 3: return format_html('{} days', days) else: return f"{days} days" days_to_expiry.short_description = 'Days to Expiry' def is_available(self, obj): if obj.is_available: return format_html('✓ Available') else: return format_html('✗ Not Available') is_available.short_description = 'Available' @admin.register(BloodTest) class BloodTestAdmin(admin.ModelAdmin): list_display = [ 'blood_unit', 'test_type', 'result', 'test_date', 'tested_by', 'verified_by', 'verified_at' ] list_filter = ['test_type', 'result', 'test_date', 'verified_at'] search_fields = ['blood_unit__unit_number', 'equipment_used', 'lot_number'] readonly_fields = ['test_date', 'verified_at'] @admin.register(CrossMatch) class CrossMatchAdmin(admin.ModelAdmin): list_display = [ 'blood_unit', 'recipient', 'test_type', 'compatibility', 'test_date', 'tested_by', 'verified_by' ] list_filter = ['test_type', 'compatibility', 'test_date'] search_fields = [ 'blood_unit__unit_number', 'recipient__first_name', 'recipient__last_name', 'recipient__patient_id' ] readonly_fields = ['test_date', 'verified_at'] class BloodIssueInline(admin.TabularInline): model = BloodIssue extra = 0 readonly_fields = ['issue_date', 'expiry_time'] @admin.register(BloodRequest) class BloodRequestAdmin(admin.ModelAdmin): list_display = [ 'request_number', 'patient', 'component_requested', 'units_requested', 'urgency', 'status', 'request_date', 'required_by', 'is_overdue' ] list_filter = [ 'urgency', 'status', 'component_requested', 'requesting_department', 'request_date', 'required_by' ] search_fields = [ 'request_number', 'patient__first_name', 'patient__last_name', 'patient__patient_id', 'indication' ] readonly_fields = ['request_date', 'processed_at'] inlines = [BloodIssueInline] fieldsets = ( ('Request Information', { 'fields': ( 'request_number', 'patient', 'requesting_department', 'requesting_physician', 'urgency', 'status' ) }), ('Blood Requirements', { 'fields': ( 'component_requested', 'units_requested', 'patient_blood_group', 'special_requirements' ) }), ('Clinical Information', { 'fields': ( 'indication', 'hemoglobin_level', 'platelet_count' ) }), ('Timeline', { 'fields': ( 'request_date', 'required_by', 'processed_by', 'processed_at' ) }), ('Additional Information', { 'fields': ('notes',), 'classes': ['collapse'] }) ) def is_overdue(self, obj): if obj.is_overdue: return format_html('✗ Overdue') else: return format_html('✓ On Time') is_overdue.short_description = 'Status' @admin.register(BloodIssue) class BloodIssueAdmin(admin.ModelAdmin): list_display = [ 'blood_unit', 'blood_request', 'issued_by', 'issued_to', 'issue_date', 'expiry_time', 'returned', 'is_expired' ] list_filter = ['returned', 'issue_date', 'expiry_time'] search_fields = [ 'blood_unit__unit_number', 'blood_request__request_number', 'blood_request__patient__first_name', 'blood_request__patient__last_name' ] readonly_fields = ['issue_date', 'is_expired'] def is_expired(self, obj): if obj.is_expired: return format_html('✗ Expired') else: return format_html('✓ Valid') is_expired.short_description = 'Status' class AdverseReactionInline(admin.TabularInline): model = AdverseReaction extra = 0 readonly_fields = ['onset_time', 'report_date'] @admin.register(Transfusion) class TransfusionAdmin(admin.ModelAdmin): list_display = [ 'blood_issue', 'start_time', 'end_time', 'status', 'volume_transfused', 'administered_by', 'duration_minutes' ] list_filter = ['status', 'start_time', 'patient_consent'] search_fields = [ 'blood_issue__blood_unit__unit_number', 'blood_issue__blood_request__patient__first_name', 'blood_issue__blood_request__patient__last_name' ] readonly_fields = ['duration_minutes'] inlines = [AdverseReactionInline] fieldsets = ( ('Transfusion Information', { 'fields': ( 'blood_issue', 'start_time', 'end_time', 'status', 'volume_transfused', 'transfusion_rate' ) }), ('Personnel', { 'fields': ('administered_by', 'witnessed_by') }), ('Consent', { 'fields': ('patient_consent', 'consent_date') }), ('Vital Signs', { 'fields': ('pre_transfusion_vitals', 'post_transfusion_vitals'), 'classes': ['collapse'] }), ('Additional Information', { 'fields': ('notes',), 'classes': ['collapse'] }) ) @admin.register(AdverseReaction) class AdverseReactionAdmin(admin.ModelAdmin): list_display = [ 'transfusion', 'reaction_type', 'severity', 'onset_time', 'reported_by', 'regulatory_reported' ] list_filter = [ 'reaction_type', 'severity', 'onset_time', 'regulatory_reported' ] search_fields = [ 'transfusion__blood_issue__blood_unit__unit_number', 'symptoms', 'treatment_given' ] readonly_fields = ['onset_time', 'report_date'] fieldsets = ( ('Reaction Information', { 'fields': ( 'transfusion', 'reaction_type', 'severity', 'onset_time', 'symptoms' ) }), ('Treatment', { 'fields': ('treatment_given', 'outcome') }), ('Reporting', { 'fields': ( 'reported_by', 'investigated_by', 'investigation_notes', 'regulatory_reported', 'report_date' ) }) ) @admin.register(InventoryLocation) class InventoryLocationAdmin(admin.ModelAdmin): list_display = [ 'name', 'location_type', 'temperature_range', 'current_stock', 'capacity', 'utilization_percentage', 'is_active' ] list_filter = ['location_type', 'is_active'] search_fields = ['name', 'notes'] def utilization_percentage(self, obj): percentage = obj.utilization_percentage if percentage >= 90: color = 'red' elif percentage >= 75: color = 'orange' else: color = 'green' return format_html( '{:.1f}%', color, percentage ) utilization_percentage.short_description = 'Utilization' @admin.register(QualityControl) class QualityControlAdmin(admin.ModelAdmin): list_display = [ 'test_type', 'test_date', 'equipment_tested', 'status', 'performed_by', 'reviewed_by', 'next_test_date' ] list_filter = ['test_type', 'status', 'test_date', 'next_test_date'] search_fields = ['equipment_tested', 'parameters_tested', 'corrective_action'] readonly_fields = ['test_date'] fieldsets = ( ('Test Information', { 'fields': ( 'test_type', 'test_date', 'equipment_tested', 'parameters_tested' ) }), ('Results', { 'fields': ( 'expected_results', 'actual_results', 'status' ) }), ('Personnel', { 'fields': ('performed_by', 'reviewed_by') }), ('Follow-up', { 'fields': ('corrective_action', 'next_test_date') }) ) # Custom admin site configuration admin.site.site_header = "Blood Bank Management System" admin.site.site_title = "Blood Bank Admin" admin.site.index_title = "Blood Bank Administration"