Marwan Alwali 2780a2dc7c update
2025-09-16 15:10:57 +03:00

430 lines
18 KiB
Python

from django import forms
from accounts.models import User
from django.utils import timezone
from datetime import timedelta
from patients.models import PatientProfile
from hr.models import Department
from .models import (
BloodGroup, Donor, BloodComponent, BloodUnit, BloodTest, CrossMatch,
BloodRequest, BloodIssue, Transfusion, AdverseReaction, InventoryLocation,
QualityControl
)
class DonorForm(forms.ModelForm):
"""Form for donor registration and updates"""
class Meta:
model = Donor
fields = [
'donor_id', 'first_name', 'last_name', 'date_of_birth',
'gender', 'blood_group', 'phone', 'email', 'address',
'emergency_contact_name', 'emergency_contact_phone',
'donor_type', 'status', 'weight', 'height', 'notes'
]
widgets = {
'date_of_birth': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}),
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
'last_name': forms.TextInput(attrs={'class': 'form-control'}),
'donor_id': forms.TextInput(attrs={'class': 'form-control'}),
'gender': forms.Select(attrs={'class': 'form-select'}),
'blood_group': forms.Select(attrs={'class': 'form-select'}),
'phone': forms.TextInput(attrs={'class': 'form-control'}),
'email': forms.EmailInput(attrs={'class': 'form-control'}),
'address': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'emergency_contact_name': forms.TextInput(attrs={'class': 'form-control'}),
'emergency_contact_phone': forms.TextInput(attrs={'class': 'form-control'}),
'donor_type': forms.Select(attrs={'class': 'form-select'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'weight': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1'}),
'height': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def clean_weight(self):
weight = self.cleaned_data.get('weight')
if weight and weight < 45.0:
raise forms.ValidationError("Minimum weight for donation is 45 kg.")
return weight
def clean_date_of_birth(self):
dob = self.cleaned_data.get('date_of_birth')
if dob:
today = timezone.now().date()
age = today.year - dob.year - ((today.month, today.day) < (dob.month, dob.day))
if age < 18:
raise forms.ValidationError("Donor must be at least 18 years old.")
if age > 65:
raise forms.ValidationError("Donor must be under 65 years old.")
return dob
class BloodUnitForm(forms.ModelForm):
"""Form for blood unit registration"""
class Meta:
model = BloodUnit
fields = [
'unit_number', 'donor', 'component', 'blood_group',
'collection_date', 'volume_ml', 'location', 'bag_type',
'anticoagulant', 'collection_site', 'notes'
]
widgets = {
'unit_number': forms.TextInput(attrs={'class': 'form-control'}),
'donor': forms.Select(attrs={'class': 'form-select'}),
'component': forms.Select(attrs={'class': 'form-select'}),
'blood_group': forms.Select(attrs={'class': 'form-select'}),
'collection_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'volume_ml': forms.NumberInput(attrs={'class': 'form-control'}),
'location': forms.TextInput(attrs={'class': 'form-control'}),
'bag_type': forms.TextInput(attrs={'class': 'form-control'}),
'anticoagulant': forms.TextInput(attrs={'class': 'form-control'}),
'collection_site': forms.TextInput(attrs={'class': 'form-control'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Filter active donors only
self.fields['donor'].queryset = Donor.objects.filter(status='active')
self.fields['component'].queryset = BloodComponent.objects.filter(is_active=True)
def save(self, commit=True):
instance = super().save(commit=False)
if not instance.expiry_date:
# Calculate expiry date based on component
component = instance.component
instance.expiry_date = instance.collection_date + timedelta(days=component.shelf_life_days)
if commit:
instance.save()
return instance
class BloodTestForm(forms.ModelForm):
"""Form for blood test results"""
class Meta:
model = BloodTest
fields = [
'blood_unit', 'test_type', 'result', 'test_date',
'equipment_used', 'lot_number', 'notes'
]
widgets = {
'blood_unit': forms.Select(attrs={'class': 'form-select'}),
'test_type': forms.Select(attrs={'class': 'form-select'}),
'result': forms.Select(attrs={'class': 'form-select'}),
'test_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'equipment_used': forms.TextInput(attrs={'class': 'form-control'}),
'lot_number': forms.TextInput(attrs={'class': 'form-control'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Filter blood units that are in testing or quarantine
self.fields['blood_unit'].queryset = BloodUnit.objects.filter(
status__in=['collected', 'testing', 'quarantine']
)
class CrossMatchForm(forms.ModelForm):
"""Form for crossmatch testing"""
class Meta:
model = CrossMatch
fields = [
'blood_unit', 'recipient', 'test_type', 'compatibility',
'test_date', 'temperature', 'incubation_time', 'notes'
]
widgets = {
'blood_unit': forms.Select(attrs={'class': 'form-select'}),
'recipient': forms.Select(attrs={'class': 'form-select'}),
'test_type': forms.Select(attrs={'class': 'form-select'}),
'compatibility': forms.Select(attrs={'class': 'form-select'}),
'test_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'temperature': forms.TextInput(attrs={'class': 'form-control'}),
'incubation_time': forms.NumberInput(attrs={'class': 'form-control'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Filter available blood units
self.fields['blood_unit'].queryset = BloodUnit.objects.filter(
status__in=['available', 'quarantine']
)
class BloodRequestForm(forms.ModelForm):
"""Form for blood transfusion requests"""
class Meta:
model = BloodRequest
fields = [
'patient', 'requesting_department', 'component_requested',
'units_requested', 'urgency', 'indication', 'special_requirements',
'patient_blood_group', 'hemoglobin_level', 'platelet_count',
'required_by'
]
widgets = {
'patient': forms.Select(attrs={'class': 'form-select'}),
'requesting_department': forms.Select(attrs={'class': 'form-select'}),
'component_requested': forms.Select(attrs={'class': 'form-select'}),
'units_requested': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'urgency': forms.Select(attrs={'class': 'form-select'}),
'indication': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'special_requirements': forms.Textarea(attrs={'class': 'form-control', 'rows': 2}),
'patient_blood_group': forms.Select(attrs={'class': 'form-select'}),
'hemoglobin_level': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1'}),
'platelet_count': forms.NumberInput(attrs={'class': 'form-control'}),
'required_by': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['component_requested'].queryset = BloodComponent.objects.filter(is_active=True)
def clean_required_by(self):
required_by = self.cleaned_data.get('required_by')
if required_by and required_by <= timezone.now():
raise forms.ValidationError("Required by date must be in the future.")
return required_by
class BloodIssueForm(forms.ModelForm):
"""Form for blood unit issuance"""
class Meta:
model = BloodIssue
fields = [
'blood_request', 'blood_unit', 'crossmatch',
'issued_to', 'expiry_time', 'notes'
]
widgets = {
'blood_request': forms.Select(attrs={'class': 'form-select'}),
'blood_unit': forms.Select(attrs={'class': 'form-select'}),
'crossmatch': forms.Select(attrs={'class': 'form-select'}),
'issued_to': forms.Select(attrs={'class': 'form-select'}),
'expiry_time': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Filter pending/processing blood requests
self.fields['blood_request'].queryset = BloodRequest.objects.filter(
status__in=['pending', 'processing', 'ready']
)
# Filter available blood units
self.fields['blood_unit'].queryset = BloodUnit.objects.filter(status='available')
# Filter staff users
self.fields['issued_to'].queryset = User.objects.filter(is_staff=True)
class TransfusionForm(forms.ModelForm):
"""Form for transfusion administration"""
class Meta:
model = Transfusion
fields = [
'blood_issue', 'start_time', 'end_time', 'status',
'volume_transfused', 'transfusion_rate', 'witnessed_by',
'patient_consent', 'consent_date', 'notes'
]
widgets = {
'blood_issue': forms.Select(attrs={'class': 'form-select'}),
'start_time': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'end_time': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'volume_transfused': forms.NumberInput(attrs={'class': 'form-control'}),
'transfusion_rate': forms.TextInput(attrs={'class': 'form-control'}),
'witnessed_by': forms.Select(attrs={'class': 'form-select'}),
'patient_consent': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'consent_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Filter issued blood units
self.fields['blood_issue'].queryset = BloodIssue.objects.filter(returned=False)
# Filter staff users for witness
self.fields['witnessed_by'].queryset = User.objects.filter(is_staff=True)
class AdverseReactionForm(forms.ModelForm):
"""Form for adverse reaction reporting"""
class Meta:
model = AdverseReaction
fields = [
'transfusion', 'reaction_type', 'severity', 'onset_time',
'symptoms', 'treatment_given', 'outcome', 'investigation_notes',
'regulatory_reported'
]
widgets = {
'transfusion': forms.Select(attrs={'class': 'form-select'}),
'reaction_type': forms.Select(attrs={'class': 'form-select'}),
'severity': forms.Select(attrs={'class': 'form-select'}),
'onset_time': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'symptoms': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'treatment_given': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'outcome': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'investigation_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'regulatory_reported': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}
class InventoryLocationForm(forms.ModelForm):
"""Form for inventory location management"""
class Meta:
model = InventoryLocation
fields = [
'name', 'location_type', 'temperature_range',
'capacity', 'is_active', 'notes'
]
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'location_type': forms.Select(attrs={'class': 'form-select'}),
'temperature_range': forms.TextInput(attrs={'class': 'form-control'}),
'capacity': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
class QualityControlForm(forms.ModelForm):
"""Form for quality control testing"""
class Meta:
model = QualityControl
fields = [
'test_type', 'test_date', 'equipment_tested',
'parameters_tested', 'expected_results', 'actual_results',
'status', 'corrective_action', 'next_test_date'
]
widgets = {
'test_type': forms.Select(attrs={'class': 'form-select'}),
'test_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
'equipment_tested': forms.TextInput(attrs={'class': 'form-control'}),
'parameters_tested': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'expected_results': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'actual_results': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'status': forms.Select(attrs={'class': 'form-select'}),
'corrective_action': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'next_test_date': forms.DateTimeInput(attrs={'type': 'datetime-local', 'class': 'form-control'}),
}
class DonorEligibilityForm(forms.Form):
"""Form for donor eligibility screening"""
# Basic health questions
feeling_well = forms.BooleanField(
label="Are you feeling well today?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
adequate_sleep = forms.BooleanField(
label="Did you get adequate sleep last night?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
eaten_today = forms.BooleanField(
label="Have you eaten within the last 4 hours?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
# Medical history
recent_illness = forms.BooleanField(
required=False,
label="Have you had any illness in the past 2 weeks?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
medications = forms.BooleanField(
required=False,
label="Are you currently taking any medications?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
recent_travel = forms.BooleanField(
required=False,
label="Have you traveled outside the country in the past 3 months?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
# Risk factors
recent_tattoo = forms.BooleanField(
required=False,
label="Have you had a tattoo or piercing in the past 12 months?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
recent_surgery = forms.BooleanField(
required=False,
label="Have you had any surgery in the past 12 months?",
widget=forms.CheckboxInput(attrs={'class': 'form-check-input'})
)
# Additional notes
notes = forms.CharField(
required=False,
label="Additional notes or concerns",
widget=forms.Textarea(attrs={'class': 'form-control', 'rows': 3})
)
def clean(self):
cleaned_data = super().clean()
# Check mandatory requirements
if not cleaned_data.get('feeling_well'):
raise forms.ValidationError("Donor must be feeling well to donate.")
if not cleaned_data.get('adequate_sleep'):
raise forms.ValidationError("Donor must have adequate sleep before donation.")
if not cleaned_data.get('eaten_today'):
raise forms.ValidationError("Donor must have eaten within the last 4 hours.")
return cleaned_data
class BloodInventorySearchForm(forms.Form):
"""Form for searching blood inventory"""
blood_group = forms.ModelChoiceField(
queryset=BloodGroup.objects.all(),
required=False,
empty_label="All Blood Groups",
widget=forms.Select(attrs={'class': 'form-select'})
)
component = forms.ModelChoiceField(
queryset=BloodComponent.objects.filter(is_active=True),
required=False,
empty_label="All Components",
widget=forms.Select(attrs={'class': 'form-select'})
)
status = forms.ChoiceField(
choices=[('', 'All Statuses')] + BloodUnit.BloodUnitStatus.choices,
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
location = forms.ModelChoiceField(
queryset=InventoryLocation.objects.filter(is_active=True),
required=False,
empty_label="All Locations",
widget=forms.Select(attrs={'class': 'form-select'})
)
expiry_days = forms.IntegerField(
required=False,
label="Expiring within (days)",
widget=forms.NumberInput(attrs={'class': 'form-control', 'min': '0'})
)