2025-10-06 15:25:37 +03:00

350 lines
16 KiB
Python

"""
Forms for Inpatients app CRUD operations.
"""
from django import forms
from django.core.exceptions import ValidationError
from django.utils import timezone
from datetime import datetime, time, timedelta
from .models import Ward, Bed, Admission, Transfer, DischargeSummary
from patients.models import PatientProfile
from accounts.models import User
class WardForm(forms.ModelForm):
"""
Form for ward management.
"""
class Meta:
model = Ward
fields = [
'name', 'description', 'ward_type', 'specialty', 'building', 'floor',
'total_beds', 'nurse_manager', 'is_active', 'is_accepting_admissions'
]
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'ward_type': forms.Select(attrs={'class': 'form-select'}),
'specialty': forms.Select(attrs={'class': 'form-select'}),
'building': forms.TextInput(attrs={'class': 'form-control'}),
'floor': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'total_beds': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'nurse_manager': forms.Select(attrs={'class': 'form-select'}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'is_accepting_admissions': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user and hasattr(user, 'tenant'):
self.fields['nurse_manager'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
employee_profile__role__in=['NURSE', 'NURSE_MANAGER']
).order_by('last_name', 'first_name')
def clean_total_beds(self):
total_beds = self.cleaned_data.get('total_beds')
if total_beds and total_beds < 1:
raise ValidationError('Ward must have at least 1 bed.')
if total_beds and total_beds > 200:
raise ValidationError('Ward capacity cannot exceed 200 beds.')
return total_beds
class BedForm(forms.ModelForm):
"""
Form for bed management.
"""
class Meta:
model = Bed
fields = [
'ward', 'bed_number', 'room_number', 'bed_type', 'room_type', 'status',
'equipment', 'features', 'maintenance_notes'
]
widgets = {
'ward': forms.Select(attrs={'class': 'form-select'}),
'bed_number': forms.TextInput(attrs={'class': 'form-control'}),
'room_number': forms.TextInput(attrs={'class': 'form-control'}),
'bed_type': forms.Select(attrs={'class': 'form-select'}),
'room_type': forms.Select(attrs={'class': 'form-select'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'equipment': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'features': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'maintenance_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user and hasattr(user, 'tenant'):
self.fields['ward'].queryset = Ward.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('name')
def clean(self):
cleaned_data = super().clean()
ward = cleaned_data.get('ward')
bed_number = cleaned_data.get('bed_number')
room_number = cleaned_data.get('room_number')
if ward and bed_number:
# Check for duplicate bed numbers within the same ward and room
existing_bed = Bed.objects.filter(
ward=ward,
room_number=room_number,
bed_number=bed_number
).exclude(pk=self.instance.pk if self.instance else None)
if existing_bed.exists():
raise ValidationError(f'Bed number {bed_number} already exists in room {room_number} of {ward.name}.')
return cleaned_data
class AdmissionForm(forms.ModelForm):
"""
Form for admission management.
"""
class Meta:
model = Admission
fields = [
'patient', 'current_ward', 'current_bed', 'admission_datetime', 'admission_type',
'admission_source', 'priority', 'admitting_physician', 'attending_physician',
'chief_complaint', 'admitting_diagnosis', 'estimated_length_of_stay',
'insurance_verified', 'authorization_number', 'status', 'admission_notes'
]
widgets = {
'patient': forms.Select(attrs={'class': 'form-select'}),
'current_ward': forms.Select(attrs={'class': 'form-select'}),
'current_bed': forms.Select(attrs={'class': 'form-select'}),
'admission_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
'admission_type': forms.Select(attrs={'class': 'form-select'}),
'admission_source': forms.Select(attrs={'class': 'form-select'}),
'priority': forms.Select(attrs={'class': 'form-select'}),
'admitting_physician': forms.Select(attrs={'class': 'form-select'}),
'attending_physician': forms.Select(attrs={'class': 'form-select'}),
'chief_complaint': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'admitting_diagnosis': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'estimated_length_of_stay': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'insurance_verified': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'authorization_number': forms.TextInput(attrs={'class': 'form-control'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'admission_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user and hasattr(user, 'tenant'):
self.fields['patient'].queryset = PatientProfile.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('last_name', 'first_name')
self.fields['current_ward'].queryset = Ward.objects.filter(
tenant=user.tenant,
is_active=True,
is_accepting_admissions=True
).order_by('name')
self.fields['current_bed'].queryset = Bed.objects.filter(
ward__tenant=user.tenant,
status='AVAILABLE'
).select_related('ward').order_by('ward__name', 'room_number', 'bed_number')
self.fields['admitting_physician'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
employee_profile__role__in=['PHYSICIAN', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
self.fields['attending_physician'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
employee_profile__role__in=['PHYSICIAN', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
def clean_admission_datetime(self):
admission_datetime = self.cleaned_data.get('admission_datetime')
if admission_datetime and admission_datetime > timezone.now() + timedelta(days=30):
raise ValidationError('Admission cannot be scheduled more than 30 days in advance.')
return admission_datetime
def clean_estimated_length_of_stay(self):
length_of_stay = self.cleaned_data.get('estimated_length_of_stay')
if length_of_stay and length_of_stay < 1:
raise ValidationError('Estimated length of stay must be at least 1 day.')
if length_of_stay and length_of_stay > 365:
raise ValidationError('Estimated length of stay cannot exceed 365 days.')
return length_of_stay
def clean(self):
cleaned_data = super().clean()
current_bed = cleaned_data.get('current_bed')
status = cleaned_data.get('status')
if status == 'ADMITTED' and not current_bed:
raise ValidationError('A bed must be assigned for admitted patients.')
if current_bed and current_bed.status != 'AVAILABLE' and not self.instance.pk:
raise ValidationError(f'Selected bed is not available. Current status: {current_bed.get_status_display()}')
return cleaned_data
class TransferForm(forms.ModelForm):
"""
Form for transfer management.
"""
class Meta:
model = Transfer
fields = [
'admission', 'patient', 'transfer_type', 'from_ward', 'from_bed',
'to_ward', 'to_bed', 'scheduled_datetime',
'reason', 'priority', 'status', 'requested_by', 'approved_by',
'transport_method', 'patient_condition', 'notes'
]
widgets = {
'admission': forms.Select(attrs={'class': 'form-select'}),
'patient': forms.Select(attrs={'class': 'form-select', 'disabled': 'disabled'}),
'transfer_type': forms.Select(attrs={'class': 'form-select'}),
'from_ward': forms.Select(attrs={'class': 'form-select', 'disabled': 'disabled'}),
'from_bed': forms.Select(attrs={'class': 'form-select', 'disabled': 'disabled'}),
'to_ward': forms.Select(attrs={'class': 'form-select'}),
'to_bed': forms.Select(attrs={'class': 'form-select'}),
'requested_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
'scheduled_datetime': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
'reason': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'priority': forms.Select(attrs={'class': 'form-select'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'requested_by': forms.Select(attrs={'class': 'form-select'}),
'approved_by': forms.Select(attrs={'class': 'form-select'}),
'transport_method': forms.Select(attrs={'class': 'form-select'}),
'patient_condition': forms.Select(attrs={'class': 'form-select'}),
'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user and hasattr(user, 'tenant'):
self.fields['admission'].queryset = Admission.objects.filter(
tenant=user.tenant,
status='ADMITTED'
).select_related('patient', 'current_ward', 'current_bed').order_by('patient__last_name', 'patient__first_name')
self.fields['patient'].queryset = PatientProfile.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('last_name', 'first_name')
self.fields['to_ward'].queryset = Ward.objects.filter(
tenant=user.tenant,
is_active=True,
is_accepting_admissions=True
).order_by('name')
self.fields['to_bed'].queryset = Bed.objects.filter(
ward__tenant=user.tenant,
status='AVAILABLE'
).select_related('ward').order_by('ward__name', 'room_number', 'bed_number')
self.fields['requested_by'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('last_name', 'first_name')
self.fields['approved_by'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
role__in=['DOCTOR', 'NURSE_MANAGER', 'ADMIN']
).order_by('last_name', 'first_name')
def clean(self):
cleaned_data = super().clean()
from_bed = cleaned_data.get('from_bed')
to_bed = cleaned_data.get('to_bed')
scheduled_datetime = cleaned_data.get('scheduled_datetime')
if from_bed and to_bed and from_bed == to_bed:
raise ValidationError('From bed and to bed cannot be the same.')
if scheduled_datetime and scheduled_datetime < timezone.now():
raise ValidationError('Scheduled transfer time cannot be in the past.')
return cleaned_data
class DischargeSummaryForm(forms.ModelForm):
"""
Form for discharge summary management.
"""
class Meta:
model = DischargeSummary
fields = [
'admission', 'discharge_date', 'discharge_time', 'length_of_stay',
'admission_diagnosis', 'final_diagnosis', 'secondary_diagnoses',
'procedures_performed', 'hospital_course', 'complications',
'discharge_medications', 'medication_changes', 'follow_up_instructions',
'discharge_disposition', 'discharge_location', 'discharging_physician',
'summary_completed', 'summary_signed', 'patient_copy_provided'
]
widgets = {
'admission': forms.Select(attrs={'class': 'form-select'}),
'discharge_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'discharge_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time'}),
'length_of_stay': forms.NumberInput(attrs={'class': 'form-control', 'readonly': 'readonly'}),
'admission_diagnosis': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'final_diagnosis': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'secondary_diagnoses': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'procedures_performed': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'hospital_course': forms.Textarea(attrs={'class': 'form-control', 'rows': 5}),
'complications': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'discharge_medications': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'medication_changes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'follow_up_instructions': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'discharge_disposition': forms.Select(attrs={'class': 'form-select'}),
'discharge_location': forms.TextInput(attrs={'class': 'form-control'}),
'discharging_physician': forms.Select(attrs={'class': 'form-select'}),
'summary_completed': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'summary_signed': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'patient_copy_provided': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user and hasattr(user, 'tenant'):
self.fields['admission'].queryset = Admission.objects.filter(
tenant=user.tenant,
status='ADMITTED'
).select_related('patient', 'current_ward', 'current_bed').order_by('patient__last_name', 'patient__first_name')
self.fields['discharging_physician'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
employee_profile__role__in=['DOCTOR', 'SPECIALIST', 'PHYSICIAN']
).order_by('last_name', 'first_name')
def clean(self):
cleaned_data = super().clean()
discharge_date = cleaned_data.get('discharge_date')
summary_signed = cleaned_data.get('summary_signed')
discharging_physician = cleaned_data.get('discharging_physician')
if discharge_date and discharge_date > timezone.now().date() + timedelta(days=1):
raise ValidationError('Discharge date cannot be more than 1 day in the future.')
if summary_signed and not discharging_physician:
raise ValidationError('A discharging physician must be specified to sign the summary.')
return cleaned_data