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'}) )