# """ # Viewflow workflows for appointments app. # Provides appointment scheduling, confirmation, and queue management workflows. # """ # # from viewflow import Flow, lock # from viewflow.base import this, flow_func # from viewflow.contrib import celery # from viewflow.decorators import flow_view # from viewflow.fields import CharField, ModelField # from viewflow.forms import ModelForm # from viewflow.views import CreateProcessView, UpdateProcessView # from viewflow.models import Process, Task # from django.contrib.auth.models import User # from django.urls import reverse_lazy # from django.utils import timezone # from django.db import transaction # from django.core.mail import send_mail # # from .models import AppointmentRequest, SlotAvailability, WaitingQueue, QueueEntry # from .views import ( # AppointmentRequestView, AvailabilityCheckView, AppointmentSchedulingView, # AppointmentConfirmationView, ReminderView, CheckInView, QueueManagementView, # TelemedicineSetupView, AppointmentCompletionView, ReschedulingView, # CancellationView, NoShowHandlingView # ) # # # class AppointmentSchedulingProcess(Process): # """ # Viewflow process model for appointment scheduling # """ # appointment_request = ModelField(AppointmentRequest, help_text='Associated appointment request') # # # Process status tracking # request_submitted = models.BooleanField(default=False) # availability_checked = models.BooleanField(default=False) # appointment_scheduled = models.BooleanField(default=False) # confirmation_sent = models.BooleanField(default=False) # reminders_scheduled = models.BooleanField(default=False) # patient_checked_in = models.BooleanField(default=False) # appointment_completed = models.BooleanField(default=False) # # class Meta: # verbose_name = 'Appointment Scheduling Process' # verbose_name_plural = 'Appointment Scheduling Processes' # # # class AppointmentSchedulingFlow(Flow): # """ # Appointment Scheduling Workflow # # This flow manages the complete appointment lifecycle from # request through scheduling, confirmation, and completion. # """ # # process_class = AppointmentSchedulingProcess # # # Flow definition # start = ( # flow_func(this.start_appointment_scheduling) # .Next(this.submit_request) # ) # # submit_request = ( # flow_view(AppointmentRequestView) # .Permission('appointments.can_submit_requests') # .Next(this.check_availability) # ) # # check_availability = ( # flow_view(AvailabilityCheckView) # .Permission('appointments.can_check_availability') # .Next(this.schedule_appointment) # ) # # schedule_appointment = ( # flow_view(AppointmentSchedulingView) # .Permission('appointments.can_schedule_appointments') # .Next(this.send_confirmation) # ) # # send_confirmation = ( # flow_view(AppointmentConfirmationView) # .Permission('appointments.can_send_confirmations') # .Next(this.schedule_reminders) # ) # # schedule_reminders = ( # flow_func(this.setup_reminders) # .Next(this.check_in_patient) # ) # # check_in_patient = ( # flow_view(CheckInView) # .Permission('appointments.can_check_in_patients') # .Next(this.complete_appointment) # ) # # complete_appointment = ( # flow_func(this.finalize_appointment) # .Next(this.end) # ) # # end = flow_func(this.end_appointment_scheduling) # # # Flow functions # def start_appointment_scheduling(self, activation): # """Initialize the appointment scheduling process""" # process = activation.process # appointment = process.appointment_request # # # Update appointment status # appointment.status = 'REQUESTED' # appointment.save() # # # Send notification to scheduling staff # self.notify_scheduling_staff(appointment) # # # Check for urgent appointments # if appointment.priority in ['HIGH', 'URGENT'] or appointment.urgency_score >= 8: # self.notify_urgent_appointment(appointment) # # def setup_reminders(self, activation): # """Setup appointment reminders""" # process = activation.process # appointment = process.appointment_request # # # Mark reminders as scheduled # process.reminders_scheduled = True # process.save() # # # Schedule reminder tasks # self.schedule_appointment_reminders(appointment) # # # Send immediate confirmation if telemedicine # if appointment.is_telemedicine: # self.setup_telemedicine_meeting(appointment) # # def finalize_appointment(self, activation): # """Finalize the appointment process""" # process = activation.process # appointment = process.appointment_request # # # Update appointment status # appointment.status = 'COMPLETED' # appointment.completed_at = timezone.now() # appointment.save() # # # Mark process as completed # process.appointment_completed = True # process.save() # # # Send completion notifications # self.notify_appointment_completion(appointment) # # # Update provider schedule # self.update_provider_schedule(appointment) # # # Generate follow-up recommendations # self.generate_follow_up_recommendations(appointment) # # def end_appointment_scheduling(self, activation): # """End the appointment scheduling workflow""" # process = activation.process # # # Generate appointment summary # self.generate_appointment_summary(process.appointment_request) # # # Helper methods # def notify_scheduling_staff(self, appointment): # """Notify scheduling staff of new appointment request""" # from django.contrib.auth.models import Group # # scheduling_staff = User.objects.filter( # groups__name='Scheduling Staff' # ) # # for staff in scheduling_staff: # send_mail( # subject=f'New Appointment Request: {appointment.patient.get_full_name()}', # message=f'New {appointment.get_appointment_type_display()} appointment request for {appointment.specialty}.', # from_email='scheduling@hospital.com', # recipient_list=[staff.email], # fail_silently=True # ) # # def notify_urgent_appointment(self, appointment): # """Notify of urgent appointment request""" # scheduling_managers = User.objects.filter( # groups__name='Scheduling Managers' # ) # # for manager in scheduling_managers: # send_mail( # subject=f'URGENT Appointment Request: {appointment.patient.get_full_name()}', # message=f'{appointment.get_priority_display()} appointment request requires immediate attention.', # from_email='scheduling@hospital.com', # recipient_list=[manager.email], # fail_silently=True # ) # # def schedule_appointment_reminders(self, appointment): # """Schedule appointment reminder tasks""" # if appointment.scheduled_datetime: # # Schedule 24-hour reminder # reminder_24h = appointment.scheduled_datetime - timedelta(hours=24) # if reminder_24h > timezone.now(): # send_appointment_reminder.apply_async( # args=[appointment.id, '24_hour'], # eta=reminder_24h # ) # # # Schedule 2-hour reminder # reminder_2h = appointment.scheduled_datetime - timedelta(hours=2) # if reminder_2h > timezone.now(): # send_appointment_reminder.apply_async( # args=[appointment.id, '2_hour'], # eta=reminder_2h # ) # # def setup_telemedicine_meeting(self, appointment): # """Setup telemedicine meeting details""" # # This would integrate with telemedicine platforms # pass # # def notify_appointment_completion(self, appointment): # """Notify appointment completion""" # # Notify patient # if appointment.patient.email: # send_mail( # subject='Appointment Completed', # message=f'Your appointment with {appointment.provider.get_full_name()} has been completed.', # from_email='appointments@hospital.com', # recipient_list=[appointment.patient.email], # fail_silently=True # ) # # def update_provider_schedule(self, appointment): # """Update provider schedule after appointment""" # # This would update provider availability # pass # # def generate_follow_up_recommendations(self, appointment): # """Generate follow-up appointment recommendations""" # # This would analyze appointment and suggest follow-ups # pass # # def generate_appointment_summary(self, appointment): # """Generate appointment summary""" # # This would generate appointment summary report # pass # # # class AppointmentConfirmationProcess(Process): # """ # Viewflow process model for appointment confirmation # """ # appointment_request = ModelField(AppointmentRequest, help_text='Associated appointment request') # # # Process status tracking # confirmation_requested = models.BooleanField(default=False) # patient_contacted = models.BooleanField(default=False) # confirmation_received = models.BooleanField(default=False) # details_updated = models.BooleanField(default=False) # confirmation_completed = models.BooleanField(default=False) # # class Meta: # verbose_name = 'Appointment Confirmation Process' # verbose_name_plural = 'Appointment Confirmation Processes' # # # class AppointmentConfirmationFlow(Flow): # """ # Appointment Confirmation Workflow # # This flow manages appointment confirmation including patient # contact, confirmation receipt, and detail updates. # """ # # process_class = AppointmentConfirmationProcess # # # Flow definition # start = ( # flow_func(this.start_confirmation) # .Next(this.request_confirmation) # ) # # request_confirmation = ( # flow_func(this.send_confirmation_request) # .Next(this.contact_patient) # ) # # contact_patient = ( # flow_view(PatientContactView) # .Permission('appointments.can_contact_patients') # .Next(this.receive_confirmation) # ) # # receive_confirmation = ( # flow_view(ConfirmationReceiptView) # .Permission('appointments.can_receive_confirmations') # .Next(this.update_details) # ) # # update_details = ( # flow_view(DetailUpdateView) # .Permission('appointments.can_update_details') # .Next(this.complete_confirmation) # ) # # complete_confirmation = ( # flow_func(this.finalize_confirmation) # .Next(this.end) # ) # # end = flow_func(this.end_confirmation) # # # Flow functions # def start_confirmation(self, activation): # """Initialize the confirmation process""" # process = activation.process # appointment = process.appointment_request # # # Send confirmation request # self.send_confirmation_request(appointment) # # def send_confirmation_request(self, activation): # """Send confirmation request to patient""" # process = activation.process # appointment = process.appointment_request # # # Mark confirmation requested # process.confirmation_requested = True # process.save() # # # Send confirmation request via preferred method # self.send_confirmation_via_preferred_method(appointment) # # def finalize_confirmation(self, activation): # """Finalize the confirmation process""" # process = activation.process # appointment = process.appointment_request # # # Update appointment status # appointment.status = 'CONFIRMED' # appointment.save() # # # Mark process as completed # process.confirmation_completed = True # process.save() # # # Send confirmation completion notification # self.notify_confirmation_completion(appointment) # # def end_confirmation(self, activation): # """End the confirmation workflow""" # process = activation.process # # # Log confirmation completion # self.log_confirmation_completion(process.appointment_request) # # # Helper methods # def send_confirmation_via_preferred_method(self, appointment): # """Send confirmation via patient's preferred method""" # preferences = appointment.reminder_preferences # # if preferences.get('email', True) and appointment.patient.email: # self.send_email_confirmation(appointment) # # if preferences.get('sms', False) and appointment.patient.phone: # self.send_sms_confirmation(appointment) # # if preferences.get('phone', False): # self.schedule_phone_confirmation(appointment) # # def send_email_confirmation(self, appointment): # """Send email confirmation""" # send_mail( # subject='Appointment Confirmation Required', # message=f'Please confirm your appointment on {appointment.scheduled_datetime}.', # from_email='appointments@hospital.com', # recipient_list=[appointment.patient.email], # fail_silently=True # ) # # def send_sms_confirmation(self, appointment): # """Send SMS confirmation""" # # This would integrate with SMS service # pass # # def schedule_phone_confirmation(self, appointment): # """Schedule phone confirmation call""" # # This would schedule a phone call task # pass # # def notify_confirmation_completion(self, appointment): # """Notify confirmation completion""" # # Notify scheduling staff # scheduling_staff = User.objects.filter( # groups__name='Scheduling Staff' # ) # # for staff in scheduling_staff: # send_mail( # subject=f'Appointment Confirmed: {appointment.patient.get_full_name()}', # message=f'Patient has confirmed appointment for {appointment.scheduled_datetime}.', # from_email='appointments@hospital.com', # recipient_list=[staff.email], # fail_silently=True # ) # # def log_confirmation_completion(self, appointment): # """Log confirmation completion""" # # This would log confirmation details # pass # # # class QueueManagementProcess(Process): # """ # Viewflow process model for queue management # """ # queue_entry = ModelField(QueueEntry, help_text='Associated queue entry') # # # Process status tracking # patient_queued = models.BooleanField(default=False) # position_assigned = models.BooleanField(default=False) # wait_time_estimated = models.BooleanField(default=False) # patient_called = models.BooleanField(default=False) # service_started = models.BooleanField(default=False) # service_completed = models.BooleanField(default=False) # queue_exited = models.BooleanField(default=False) # # class Meta: # verbose_name = 'Queue Management Process' # verbose_name_plural = 'Queue Management Processes' # # # class QueueManagementFlow(Flow): # """ # Queue Management Workflow # # This flow manages patient flow through waiting queues # including position assignment and service coordination. # """ # # process_class = QueueManagementProcess # # # Flow definition # start = ( # flow_func(this.start_queue_management) # .Next(this.queue_patient) # ) # # queue_patient = ( # flow_view(PatientQueuingView) # .Permission('appointments.can_queue_patients') # .Next(this.assign_position) # ) # # assign_position = ( # flow_func(this.calculate_queue_position) # .Next(this.estimate_wait_time) # ) # # estimate_wait_time = ( # flow_func(this.calculate_wait_time) # .Next(this.call_patient) # ) # # call_patient = ( # flow_view(PatientCallingView) # .Permission('appointments.can_call_patients') # .Next(this.start_service) # ) # # start_service = ( # flow_view(ServiceStartView) # .Permission('appointments.can_start_service') # .Next(this.complete_service) # ) # # complete_service = ( # flow_view(ServiceCompletionView) # .Permission('appointments.can_complete_service') # .Next(this.exit_queue) # ) # # exit_queue = ( # flow_func(this.finalize_queue_management) # .Next(this.end) # ) # # end = flow_func(this.end_queue_management) # # # Flow functions # def start_queue_management(self, activation): # """Initialize the queue management process""" # process = activation.process # entry = process.queue_entry # # # Update entry status # entry.status = 'WAITING' # entry.save() # # # Send queue notification # self.notify_queue_entry(entry) # # def calculate_queue_position(self, activation): # """Calculate and assign queue position""" # process = activation.process # entry = process.queue_entry # # # Calculate position based on priority and arrival time # position = self.determine_queue_position(entry) # entry.queue_position = position # entry.save() # # # Mark position assigned # process.position_assigned = True # process.save() # # # Update other queue positions # self.update_queue_positions(entry.queue) # # def calculate_wait_time(self, activation): # """Calculate estimated wait time""" # process = activation.process # entry = process.queue_entry # # # Calculate estimated service time # estimated_time = self.estimate_service_time(entry) # entry.estimated_service_time = estimated_time # entry.save() # # # Mark wait time estimated # process.wait_time_estimated = True # process.save() # # # Send wait time notification # self.notify_wait_time(entry) # # def finalize_queue_management(self, activation): # """Finalize the queue management process""" # process = activation.process # entry = process.queue_entry # # # Update entry status # entry.status = 'COMPLETED' # entry.served_at = timezone.now() # entry.save() # # # Mark process as completed # process.queue_exited = True # process.save() # # # Update queue metrics # self.update_queue_metrics(entry) # # # Notify next patient # self.notify_next_patient(entry.queue) # # def end_queue_management(self, activation): # """End the queue management workflow""" # process = activation.process # # # Generate queue summary # self.generate_queue_summary(process.queue_entry) # # # Helper methods # def notify_queue_entry(self, entry): # """Notify patient of queue entry""" # if entry.patient.email: # send_mail( # subject='Added to Waiting Queue', # message=f'You have been added to the {entry.queue.name} queue.', # from_email='appointments@hospital.com', # recipient_list=[entry.patient.email], # fail_silently=True # ) # # def determine_queue_position(self, entry): # """Determine queue position based on priority""" # # This would implement priority-based positioning logic # return entry.queue.current_queue_size + 1 # # def update_queue_positions(self, queue): # """Update positions for all entries in queue""" # entries = queue.queue_entries.filter(status='WAITING').order_by('priority_score', 'joined_at') # for i, entry in enumerate(entries, 1): # entry.queue_position = i # entry.save() # # def estimate_service_time(self, entry): # """Estimate service time for queue entry""" # queue = entry.queue # position = entry.queue_position # avg_service_time = queue.average_service_time_minutes # # estimated_minutes = position * avg_service_time # return timezone.now() + timedelta(minutes=estimated_minutes) # # def notify_wait_time(self, entry): # """Notify patient of estimated wait time""" # if entry.patient.email and entry.estimated_service_time: # wait_minutes = int((entry.estimated_service_time - timezone.now()).total_seconds() / 60) # send_mail( # subject='Queue Wait Time Update', # message=f'Your estimated wait time is {wait_minutes} minutes.', # from_email='appointments@hospital.com', # recipient_list=[entry.patient.email], # fail_silently=True # ) # # def update_queue_metrics(self, entry): # """Update queue performance metrics""" # # This would update queue analytics # pass # # def notify_next_patient(self, queue): # """Notify next patient in queue""" # next_entry = queue.queue_entries.filter( # status='WAITING' # ).order_by('queue_position').first() # # if next_entry: # self.notify_patient_ready(next_entry) # # def notify_patient_ready(self, entry): # """Notify patient they are ready to be called""" # if entry.patient.email: # send_mail( # subject='Ready for Appointment', # message='You will be called shortly for your appointment.', # from_email='appointments@hospital.com', # recipient_list=[entry.patient.email], # fail_silently=True # ) # # def generate_queue_summary(self, entry): # """Generate queue management summary""" # # This would generate queue analytics # pass # # # class TelemedicineSetupProcess(Process): # """ # Viewflow process model for telemedicine setup # """ # appointment_request = ModelField(AppointmentRequest, help_text='Associated appointment request') # # # Process status tracking # platform_selected = models.BooleanField(default=False) # meeting_created = models.BooleanField(default=False) # credentials_sent = models.BooleanField(default=False) # technical_check_completed = models.BooleanField(default=False) # meeting_ready = models.BooleanField(default=False) # # class Meta: # verbose_name = 'Telemedicine Setup Process' # verbose_name_plural = 'Telemedicine Setup Processes' # # # class TelemedicineSetupFlow(Flow): # """ # Telemedicine Setup Workflow # # This flow manages telemedicine appointment setup including # platform selection, meeting creation, and technical verification. # """ # # process_class = TelemedicineSetupProcess # # # Flow definition # start = ( # flow_func(this.start_telemedicine_setup) # .Next(this.select_platform) # ) # # select_platform = ( # flow_view(PlatformSelectionView) # .Permission('appointments.can_select_platforms') # .Next(this.create_meeting) # ) # # create_meeting = ( # flow_view(MeetingCreationView) # .Permission('appointments.can_create_meetings') # .Next(this.send_credentials) # ) # # send_credentials = ( # flow_view(CredentialSendingView) # .Permission('appointments.can_send_credentials') # .Next(this.technical_check) # ) # # technical_check = ( # flow_view(TechnicalCheckView) # .Permission('appointments.can_perform_technical_checks') # .Next(this.finalize_setup) # ) # # finalize_setup = ( # flow_func(this.complete_telemedicine_setup) # .Next(this.end) # ) # # end = flow_func(this.end_telemedicine_setup) # # # Flow functions # def start_telemedicine_setup(self, activation): # """Initialize the telemedicine setup process""" # process = activation.process # appointment = process.appointment_request # # # Mark as telemedicine appointment # appointment.is_telemedicine = True # appointment.save() # # # Send setup notification # self.notify_telemedicine_setup(appointment) # # def complete_telemedicine_setup(self, activation): # """Finalize the telemedicine setup process""" # process = activation.process # appointment = process.appointment_request # # # Mark meeting as ready # process.meeting_ready = True # process.save() # # # Send final meeting details # self.send_final_meeting_details(appointment) # # # Schedule pre-meeting reminder # self.schedule_pre_meeting_reminder(appointment) # # def end_telemedicine_setup(self, activation): # """End the telemedicine setup workflow""" # process = activation.process # # # Log setup completion # self.log_telemedicine_setup(process.appointment_request) # # # Helper methods # def notify_telemedicine_setup(self, appointment): # """Notify patient of telemedicine setup""" # if appointment.patient.email: # send_mail( # subject='Telemedicine Appointment Setup', # message='Your telemedicine appointment is being set up. You will receive meeting details shortly.', # from_email='telemedicine@hospital.com', # recipient_list=[appointment.patient.email], # fail_silently=True # ) # # def send_final_meeting_details(self, appointment): # """Send final meeting details to patient""" # if appointment.patient.email: # send_mail( # subject='Telemedicine Meeting Details', # message=f'Meeting URL: {appointment.meeting_url}\nMeeting ID: {appointment.meeting_id}', # from_email='telemedicine@hospital.com', # recipient_list=[appointment.patient.email], # fail_silently=True # ) # # def schedule_pre_meeting_reminder(self, appointment): # """Schedule pre-meeting reminder""" # if appointment.scheduled_datetime: # reminder_time = appointment.scheduled_datetime - timedelta(minutes=15) # if reminder_time > timezone.now(): # send_telemedicine_reminder.apply_async( # args=[appointment.id], # eta=reminder_time # ) # # def log_telemedicine_setup(self, appointment): # """Log telemedicine setup completion""" # # This would log setup details # pass # # # # Celery tasks for background processing # @celery.job # def send_appointment_reminder(appointment_id, reminder_type): # """Background task to send appointment reminders""" # try: # appointment = AppointmentRequest.objects.get(id=appointment_id) # # if reminder_type == '24_hour': # subject = 'Appointment Reminder - Tomorrow' # message = f'Reminder: You have an appointment tomorrow at {appointment.scheduled_datetime}.' # elif reminder_type == '2_hour': # subject = 'Appointment Reminder - 2 Hours' # message = f'Reminder: You have an appointment in 2 hours at {appointment.scheduled_datetime}.' # # if appointment.patient.email: # send_mail( # subject=subject, # message=message, # from_email='appointments@hospital.com', # recipient_list=[appointment.patient.email], # fail_silently=True # ) # # return True # except Exception: # return False # # # @celery.job # def send_telemedicine_reminder(appointment_id): # """Background task to send telemedicine pre-meeting reminder""" # try: # appointment = AppointmentRequest.objects.get(id=appointment_id) # # if appointment.patient.email: # send_mail( # subject='Telemedicine Meeting Starting Soon', # message=f'Your telemedicine appointment starts in 15 minutes. Meeting URL: {appointment.meeting_url}', # from_email='telemedicine@hospital.com', # recipient_list=[appointment.patient.email], # fail_silently=True # ) # # return True # except Exception: # return False # # # @celery.job # def auto_confirm_appointments(): # """Background task to automatically confirm appointments""" # try: # # This would implement auto-confirmation logic # return True # except Exception: # return False # # # @celery.job # def manage_no_shows(): # """Background task to manage no-show appointments""" # try: # # This would identify and handle no-show appointments # return True # except Exception: # return False # # # @celery.job # def optimize_schedules(): # """Background task to optimize provider schedules""" # try: # # This would implement schedule optimization # return True # except Exception: # return False # # # @celery.job # def update_queue_positions(): # """Background task to update queue positions""" # try: # # This would update queue positions and wait times # return True # except Exception: # return False #