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