2025-08-12 13:33:25 +03:00

442 lines
18 KiB
Python

"""
Forms for Appointments 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 (
AppointmentRequest, SlotAvailability, WaitingQueue, QueueEntry,
TelemedicineSession, AppointmentTemplate
)
from patients.models import PatientProfile
from accounts.models import User
class AppointmentRequestForm(forms.ModelForm):
"""
Form for appointment request management.
"""
class Meta:
model = AppointmentRequest
fields = [
'patient', 'provider', 'appointment_type', 'specialty', 'preferred_date',
'preferred_time', 'duration_minutes', 'priority', 'chief_complaint',
'clinical_notes', 'status', 'is_telemedicine', 'location', 'room_number'
]
widgets = {
'patient': forms.Select(attrs={'class': 'form-select'}),
'provider': forms.Select(attrs={'class': 'form-select'}),
'appointment_type': forms.Select(attrs={'class': 'form-select'}),
'specialty': forms.Select(attrs={'class': 'form-select'}),
'preferred_date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'preferred_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time'}),
'duration_minutes': forms.NumberInput(attrs={'class': 'form-control', 'min': '15', 'step': '15'}),
'priority': forms.Select(attrs={'class': 'form-select'}),
'chief_complaint': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'clinical_notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'status': forms.Select(attrs={'class': 'form-select'}),
'is_telemedicine': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'location': forms.TextInput(attrs={'class': 'form-control'}),
'room_number': forms.TextInput(attrs={'class': 'form-control'}),
}
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['provider'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
def clean_preferred_date(self):
preferred_date = self.cleaned_data.get('preferred_date')
if preferred_date and preferred_date < timezone.now().date():
raise ValidationError('Appointment cannot be scheduled in the past.')
return preferred_date
def clean_duration_minutes(self):
duration = self.cleaned_data.get('duration_minutes')
if duration and duration < 15:
raise ValidationError('Appointment duration must be at least 15 minutes.')
if duration and duration > 480: # 8 hours
raise ValidationError('Appointment duration cannot exceed 8 hours.')
return duration
class SlotAvailabilityForm(forms.ModelForm):
"""
Form for slot availability management.
"""
class Meta:
model = SlotAvailability
fields = [
'provider', 'date', 'start_time', 'end_time', 'duration_minutes',
'max_appointments', 'location', 'room_number', 'specialty',
'availability_type', 'is_active', 'supports_telemedicine'
]
widgets = {
'provider': forms.Select(attrs={'class': 'form-select'}),
'date': forms.DateInput(attrs={'class': 'form-control', 'type': 'date'}),
'start_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time'}),
'end_time': forms.TimeInput(attrs={'class': 'form-control', 'type': 'time'}),
'duration_minutes': forms.NumberInput(attrs={'class': 'form-control', 'min': '15', 'step': '15'}),
'max_appointments': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'location': forms.TextInput(attrs={'class': 'form-control'}),
'room_number': forms.TextInput(attrs={'class': 'form-control'}),
'specialty': forms.TextInput(attrs={'class': 'form-control'}),
'availability_type': forms.Select(attrs={'class': 'form-select'}),
'is_active': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'supports_telemedicine': 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['provider'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
def clean(self):
cleaned_data = super().clean()
date = cleaned_data.get('date')
start_time = cleaned_data.get('start_time')
end_time = cleaned_data.get('end_time')
if date and date < timezone.now().date():
raise ValidationError('Availability date cannot be in the past.')
if start_time and end_time and start_time >= end_time:
raise ValidationError('Start time must be before end time.')
return cleaned_data
class WaitingQueueForm(forms.ModelForm):
"""
Form for waiting queue management.
"""
class Meta:
model = WaitingQueue
fields = [
'name', 'description', 'queue_type', 'location', 'specialty',
'max_queue_size', 'average_service_time_minutes', 'is_active'
]
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'queue_type': forms.Select(attrs={'class': 'form-select'}),
'location': forms.TextInput(attrs={'class': 'form-control'}),
'specialty': forms.TextInput(attrs={'class': 'form-control'}),
'max_queue_size': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'average_service_time_minutes': forms.NumberInput(attrs={'class': 'form-control', 'min': '0'}),
'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['provider'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
class QueueEntryForm(forms.ModelForm):
"""
Form for queue entry management.
"""
class Meta:
model = QueueEntry
fields = [
'queue', 'patient', 'appointment', 'priority_score', 'assigned_provider',
'status', 'notes'
]
widgets = {
'queue': forms.Select(attrs={'class': 'form-select'}),
'patient': forms.Select(attrs={'class': 'form-select'}),
'appointment': forms.Select(attrs={'class': 'form-select'}),
'priority_score': forms.NumberInput(attrs={'class': 'form-control', 'step': '0.1', 'min': '0'}),
'assigned_provider': forms.Select(attrs={'class': 'form-select'}),
'status': 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['queue'].queryset = WaitingQueue.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('name')
self.fields['patient'].queryset = PatientProfile.objects.filter(
tenant=user.tenant,
is_active=True
).order_by('last_name', 'first_name')
self.fields['appointment'].queryset = AppointmentRequest.objects.filter(
tenant=user.tenant,
status__in=['SCHEDULED', 'CONFIRMED']
).order_by('-preferred_date')
class TelemedicineSessionForm(forms.ModelForm):
"""
Form for telemedicine session management.
"""
class Meta:
model = TelemedicineSession
fields = [
'appointment', 'platform', 'meeting_url', 'meeting_id', 'meeting_password',
'scheduled_start', 'scheduled_end', 'status', 'waiting_room_enabled',
'recording_enabled', 'recording_consent'
]
widgets = {
'appointment': forms.Select(attrs={'class': 'form-select'}),
'platform': forms.Select(attrs={'class': 'form-select'}),
'meeting_url': forms.URLInput(attrs={'class': 'form-control'}),
'meeting_id': forms.TextInput(attrs={'class': 'form-control'}),
'meeting_password': forms.PasswordInput(attrs={'class': 'form-control'}),
'scheduled_start': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
'scheduled_end': forms.DateTimeInput(attrs={'class': 'form-control', 'type': 'datetime-local'}),
'status': forms.Select(attrs={'class': 'form-select'}),
'waiting_room_enabled': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'recording_enabled': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
'recording_consent': 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['appointment'].queryset = AppointmentRequest.objects.filter(
tenant=user.tenant,
is_telemedicine=True,
status__in=['SCHEDULED', 'CONFIRMED', 'IN_PROGRESS']
).order_by('-preferred_date')
def clean(self):
cleaned_data = super().clean()
start_time = cleaned_data.get('session_start_time')
end_time = cleaned_data.get('session_end_time')
if start_time and end_time and start_time >= end_time:
raise ValidationError('Start time must be before end time.')
return cleaned_data
class AppointmentTemplateForm(forms.ModelForm):
"""
Form for appointment template management.
"""
class Meta:
model = AppointmentTemplate
fields = [
'name', 'description', 'appointment_type', 'duration_minutes',
'specialty', 'advance_booking_days', 'minimum_notice_hours',
'pre_appointment_instructions', 'post_appointment_instructions',
'is_active'
]
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control'}),
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
'appointment_type': forms.TextInput(attrs={'class': 'form-control'}),
'duration_minutes': forms.NumberInput(attrs={'class': 'form-control', 'min': '15', 'step': '15'}),
'specialty': forms.TextInput(attrs={'class': 'form-control'}),
'advance_booking_days': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'minimum_notice_hours': forms.NumberInput(attrs={'class': 'form-control', 'min': '1'}),
'pre_appointment_instructions': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'post_appointment_instructions': forms.Textarea(attrs={'class': 'form-control', 'rows': 4}),
'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['provider'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
class AppointmentSearchForm(forms.Form):
"""
Form for searching appointment data.
"""
search = forms.CharField(
max_length=255,
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Search appointments, patients, providers...'
})
)
appointment_type = forms.ChoiceField(
choices=[('', 'All Types')] + list(AppointmentRequest._meta.get_field('appointment_type').choices),
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
status = forms.ChoiceField(
choices=[('', 'All Status')] + list(AppointmentRequest._meta.get_field('status').choices),
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
priority = forms.ChoiceField(
choices=[('', 'All Priorities')] + list(AppointmentRequest._meta.get_field('priority').choices),
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
provider = forms.ModelChoiceField(
queryset=User.objects.none(),
required=False,
empty_label='All Providers',
widget=forms.Select(attrs={'class': 'form-select'})
)
specialty = forms.CharField(
max_length=100,
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Specialty'
})
)
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'
})
)
is_telemedicine = forms.ChoiceField(
choices=[
('', 'All Appointments'),
('true', 'Telemedicine Only'),
('false', 'In-Person Only')
],
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user and hasattr(user, 'tenant'):
self.fields['provider'].queryset = User.objects.filter(
tenant=user.tenant,
is_active=True,
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')
class QueueSearchForm(forms.Form):
"""
Form for searching queues.
"""
search = forms.CharField(
max_length=200,
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Search queues...'
})
)
queue_type = forms.ChoiceField(
choices=[('', 'All Types')] + [
('PROVIDER', 'Provider Queue'),
('SPECIALTY', 'Specialty Queue'),
('LOCATION', 'Location Queue'),
('PROCEDURE', 'Procedure Queue'),
('EMERGENCY', 'Emergency Queue'),
],
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
is_active = forms.ChoiceField(
choices=[('', 'All'), ('true', 'Active'), ('false', 'Inactive')],
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
class SlotSearchForm(forms.Form):
"""
Form for searching appointment slots.
"""
search = forms.CharField(
max_length=200,
required=False,
widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Search slots...'
})
)
provider = forms.ModelChoiceField(
queryset=User.objects.none(),
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'
})
)
is_available = forms.ChoiceField(
choices=[('', 'All'), ('true', 'Available'), ('false', 'Booked')],
required=False,
widget=forms.Select(attrs={'class': 'form-select'})
)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super().__init__(*args, **kwargs)
if user:
self.fields['provider'].queryset = User.objects.filter(
tenant=user.tenant,
role__in=['PHYSICIAN', 'NURSE', 'NURSE_PRACTITIONER', 'PHYSICIAN_ASSISTANT']
).order_by('last_name', 'first_name')