Marwan Alwali 610e165e17 update
2025-09-04 19:19:52 +03:00

388 lines
12 KiB
Python

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('<span style="color: green;">✓ Eligible</span>')
else:
return format_html('<span style="color: red;">✗ Not Eligible</span>')
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('<span style="color: red;">Expired</span>')
elif days <= 3:
return format_html('<span style="color: orange;">{} days</span>', 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('<span style="color: green;">✓ Available</span>')
else:
return format_html('<span style="color: red;">✗ Not Available</span>')
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('<span style="color: red;">✗ Overdue</span>')
else:
return format_html('<span style="color: green;">✓ On Time</span>')
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('<span style="color: red;">✗ Expired</span>')
else:
return format_html('<span style="color: green;">✓ Valid</span>')
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(
'<span style="color: {};">{:.1f}%</span>',
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"