291 lines
13 KiB
Python
291 lines
13 KiB
Python
"""
|
|
Forms for Patients app CRUD operations.
|
|
"""
|
|
|
|
from django import forms
|
|
from django.core.exceptions import ValidationError
|
|
from django.utils import timezone
|
|
from .models import (
|
|
PatientProfile, EmergencyContact, InsuranceInfo,
|
|
ConsentTemplate, ConsentForm, PatientNote
|
|
)
|
|
|
|
|
|
class PatientProfileForm(forms.ModelForm):
|
|
"""
|
|
Form for patient profile management.
|
|
"""
|
|
class Meta:
|
|
model = PatientProfile
|
|
fields = [
|
|
'first_name', 'last_name', 'middle_name', 'preferred_name', 'suffix',
|
|
'id_number', 'date_of_birth', 'gender', 'phone_number', 'mobile_number',
|
|
'email', 'address_line_1', 'address_line_2', 'city', 'state', 'zip_code',
|
|
'country', 'marital_status', 'occupation', 'is_active'
|
|
]
|
|
widgets = {
|
|
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'last_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'middle_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'preferred_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'suffix': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'id_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'date_of_birth': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'gender': forms.Select(attrs={'class': 'form-select'}),
|
|
'phone_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'mobile_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
|
'address_line_1': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'address_line_2': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'city': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'state': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'zip_code': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'country': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'marital_status': forms.Select(attrs={'class': 'form-select'}),
|
|
'occupation': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
}
|
|
|
|
def clean_date_of_birth(self):
|
|
date_of_birth = self.cleaned_data.get('date_of_birth')
|
|
if date_of_birth and date_of_birth > timezone.now().date():
|
|
raise ValidationError('Date of birth cannot be in the future.')
|
|
return date_of_birth
|
|
|
|
def clean_email(self):
|
|
email = self.cleaned_data.get('email')
|
|
if email:
|
|
# Check for duplicate email within tenant
|
|
tenant = getattr(self.instance, 'tenant', None)
|
|
if tenant:
|
|
existing = PatientProfile.objects.filter(
|
|
tenant=tenant,
|
|
email=email
|
|
).exclude(pk=self.instance.pk if self.instance.pk else None)
|
|
if existing.exists():
|
|
raise ValidationError('A patient with this email already exists.')
|
|
return email
|
|
|
|
|
|
class EmergencyContactForm(forms.ModelForm):
|
|
"""
|
|
Form for emergency contact management.
|
|
"""
|
|
class Meta:
|
|
model = EmergencyContact
|
|
exclude = ["patient"]
|
|
fields = [
|
|
'first_name', 'last_name', 'relationship', 'phone_number',
|
|
'mobile_number', 'email', 'address_line_1', 'address_line_2', 'city',
|
|
'state', 'zip_code', 'priority', 'is_authorized_for_medical_decisions'
|
|
]
|
|
widgets = {
|
|
# 'patient': forms.Select(attrs={'class': 'form-select'}),
|
|
'first_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'last_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'relationship': forms.Select(attrs={'class': 'form-select'}),
|
|
'phone_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'mobile_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'email': forms.EmailInput(attrs={'class': 'form-control'}),
|
|
'address_line_1': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'address_line_2': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'city': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'state': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'zip_code': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'priority': forms.NumberInput(attrs={'class': 'form-control'}),
|
|
'is_authorized_for_medical_decisions': 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['patient'].queryset = PatientProfile.objects.filter(
|
|
# tenant=user.tenant,
|
|
# is_active=True
|
|
# ).order_by('last_name', 'first_name')
|
|
|
|
|
|
class InsuranceInfoForm(forms.ModelForm):
|
|
"""
|
|
Form for insurance information management.
|
|
"""
|
|
class Meta:
|
|
model = InsuranceInfo
|
|
exclude = ["patient"]
|
|
fields = [
|
|
'insurance_type', 'insurance_company', 'plan_name', 'plan_type',
|
|
'policy_number', 'group_number', 'subscriber_name', 'subscriber_relationship',
|
|
'subscriber_dob', 'subscriber_id_number', 'effective_date', 'termination_date',
|
|
'copay_amount', 'deductible_amount', 'out_of_pocket_max', 'is_verified',
|
|
'requires_authorization'
|
|
]
|
|
widgets = {
|
|
'insurance_type': forms.Select(attrs={'class': 'form-select'}),
|
|
'insurance_company': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'plan_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'plan_type': forms.Select(attrs={'class': 'form-select'}),
|
|
'policy_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'group_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'subscriber_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'subscriber_relationship': forms.Select(attrs={'class': 'form-select'}),
|
|
'subscriber_dob': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'subscriber_id_number': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'effective_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'termination_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'copay_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
|
|
'deductible_amount': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
|
|
'out_of_pocket_max': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.01'}),
|
|
'is_verified': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'requires_authorization': 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['patient'].queryset = PatientProfile.objects.filter(
|
|
# tenant=user.tenant,
|
|
# is_active=True
|
|
# ).order_by('last_name', 'first_name')
|
|
|
|
|
|
class ConsentTemplateForm(forms.ModelForm):
|
|
"""
|
|
Form for consent template management.
|
|
"""
|
|
class Meta:
|
|
model = ConsentTemplate
|
|
fields = [
|
|
'name', 'description', 'category', 'content', 'version',
|
|
'is_active', 'requires_signature', 'requires_witness', 'requires_guardian',
|
|
'effective_date', 'expiry_date',
|
|
]
|
|
widgets = {
|
|
'name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'category': forms.Select(attrs={'class': 'form-select'}),
|
|
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
|
|
'version': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'requires_signature': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'requires_witness': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'requires_guardian': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'effective_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
'expiry_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
|
|
}
|
|
|
|
|
|
class ConsentFormForm(forms.ModelForm):
|
|
"""
|
|
Form for consent form management.
|
|
"""
|
|
class Meta:
|
|
model = ConsentForm
|
|
fields = [
|
|
'patient', 'template', 'status', 'patient_signature', 'patient_signed_at',
|
|
'guardian_signature', 'guardian_signed_at', 'guardian_name', 'guardian_relationship',
|
|
'witness_signature', 'witness_signed_at', 'witness_name', 'notes'
|
|
]
|
|
widgets = {
|
|
'patient': forms.Select(attrs={'class': 'form-select'}),
|
|
'template': forms.Select(attrs={'class': 'form-select'}),
|
|
'status': forms.Select(attrs={'class': 'form-select'}),
|
|
'patient_signature': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'patient_signed_at': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'guardian_signature': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'guardian_signed_at': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'guardian_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'guardian_relationship': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'witness_signature': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
|
'witness_signed_at': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
|
|
'witness_name': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'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['template'].queryset = ConsentTemplate.objects.filter(
|
|
tenant=user.tenant,
|
|
is_active=True
|
|
).order_by('name')
|
|
|
|
|
|
class PatientNoteForm(forms.ModelForm):
|
|
"""
|
|
Form for patient note management.
|
|
"""
|
|
class Meta:
|
|
model = PatientNote
|
|
exclude = ["patient"]
|
|
fields = [
|
|
'title', 'content', 'category', 'priority',
|
|
'is_confidential', 'is_alert', 'is_active'
|
|
]
|
|
widgets = {
|
|
# 'patient': forms.Select(attrs={'class': 'form-select'}),
|
|
'title': forms.TextInput(attrs={'class': 'form-control'}),
|
|
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 6}),
|
|
'category': forms.Select(attrs={'class': 'form-select'}),
|
|
'priority': forms.Select(attrs={'class': 'form-select'}),
|
|
'is_confidential': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'is_alert': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
|
'is_active': 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['patient'].queryset = PatientProfile.objects.filter(
|
|
# tenant=user.tenant,
|
|
# is_active=True
|
|
# ).order_by('last_name', 'first_name')
|
|
|
|
|
|
class PatientsSearchForm(forms.Form):
|
|
"""
|
|
Form for searching patient data.
|
|
"""
|
|
search = forms.CharField(
|
|
max_length=255,
|
|
required=False,
|
|
widget=forms.TextInput(attrs={
|
|
'class': 'form-control',
|
|
'placeholder': 'Search patients, contacts, insurance...'
|
|
})
|
|
)
|
|
gender = forms.ChoiceField(
|
|
choices=[('', 'All Genders')] + list(PatientProfile._meta.get_field('gender').choices),
|
|
required=False,
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
status = forms.ChoiceField(
|
|
choices=[
|
|
('', 'All Statuses'),
|
|
('active', 'Active'),
|
|
('inactive', 'Inactive'),
|
|
],
|
|
required=False,
|
|
widget=forms.Select(attrs={'class': 'form-select'})
|
|
)
|
|
date_from = forms.DateField(
|
|
required=False,
|
|
widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
|
)
|
|
date_to = forms.DateField(
|
|
required=False,
|
|
widget=forms.DateInput(attrs={'class': 'form-control', 'type': 'date'})
|
|
)
|
|
|