Пример #1
0
        private void ProcessScreeningResponse(PmsAppointment appt, SmsMessage msg)
        {
            // the patient should respond with "yes" or "no" but they may not bother and just respond with "arrived"
            // of course they might respond with anything else that we can't understand, so we'll explain apologetically if they do
            if (MessageMatches(msg.message, "yes", "y"))
            {
                // twilio:
                SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_SCREENING_YES, appt, null));
                SmsSender.SendMessage(rmsg);
                LogMsg(OUT, rmsg, "process screening response 'yes'", appt);

                if (IsDoingVideo)
                {
                    // PMS:
                    appt.IsVideoConsultation = true;
                    if (VideoManager.AsksForVideoUrl())
                    {
                        AppointmentUpdater.SaveAppointmentAsVideoMeeting(appt, null, null);
                    }
                    else
                    {
                        var details = VideoManager.getConferenceDetails(appt, false);
                        AppointmentUpdater.SaveAppointmentAsVideoMeeting(appt, "Video URL: " + details, details);
                    }
                }

                // local storage
                appt.ExternalData.ScreeningMessageResponse = true;
                if (IsDoingVideo)
                {
                    appt.ExternalData.IsVideoConsultation = true;
                }
                else
                {
                    appt.IsVideoConsultation = false;
                }
                Storage.SaveAppointmentStatus(appt);
            }
            else if (MessageMatches(msg.message, "no", "n"))
            {
                SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_SCREENING_NO, appt, null));
                SmsSender.SendMessage(rmsg);
                LogMsg(OUT, rmsg, "process screening response 'no'", appt);
                appt.ExternalData.ScreeningMessageResponse = true;
                appt.IsVideoConsultation = false;
                Storage.SaveAppointmentStatus(appt);
            }
            else if (MessageMatches(msg.message, "arrived", "here", "a"))
            {
                ProcessArrivalMessage(appt, msg);
            }
            else
            {
                // we haven't understood it
                SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_DONT_UNDERSTAND_SCREENING, appt, null));
                SmsSender.SendMessage(rmsg);
                LogMsg(OUT, rmsg, "fail to process screening response", appt);
                UnprocessableMessages.Add(msg);
            }
        }
Пример #2
0
 private void ProcessArrivalMessage(PmsAppointment appt, SmsMessage msg)
 {
     if (MessageMatches(msg.message, "arrived", "here", "a"))
     {
         // twilio:
         SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_ARRIVED_THX, appt, null));
         SmsSender.SendMessage(rmsg);
         LogMsg(OUT, rmsg, "process arrival message", appt);
         // PMS:
         appt.ArrivalStatus = AppointmentStatus.Arrived;
         AppointmentUpdater.SaveAppointmentStatusValue(appt);
         // local storage
         appt.ExternalData.ScreeningMessageResponse = true;
         appt.ExternalData.ArrivalStatus            = AppointmentStatus.Arrived;
         Storage.SaveAppointmentStatus(appt);
     }
     else
     {
         // we haven't understood it
         SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_DONT_UNDERSTAND_ARRIVING, appt, null));
         SmsSender.SendMessage(rmsg);
         LogMsg(OUT, rmsg, "fail to process arrival message", appt);
         UnprocessableMessages.Add(msg);
     }
 }
Пример #3
0
 private PmsAppointment ChooseRelevantAppointment(List <PmsAppointment> candidates, SmsMessage msg)
 {
     if (candidates.Count > 2)
     {
         SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_TOO_MANY_APPOINTMENTS, candidates[0], null));
         SmsSender.SendMessage(rmsg);
         LogMsg(OUT, rmsg, "Too many candidates", candidates[0]);
         UnprocessableMessages.Add(msg);
     }
     else
     {
         // pseudo code:
         // if the two appointments are at the same time, we only care about the first one
         // otherwise, in principle we are interested in the first one, unless the message response belongs to an earlier cycle ("yes" / "no") and we're waiting for that
         var appt1 = candidates[0].AppointmentStartTime < candidates[1].AppointmentStartTime ? candidates[0] : candidates[1];
         var appt2 = candidates[0].AppointmentStartTime < candidates[1].AppointmentStartTime ? candidates[1] : candidates[0];
         if (MessageMatches(msg.message, "yes", "no"))
         {
             foreach (var appt in candidates)
             {
                 if (appt.ExternalData.ScreeningMessageSent && !appt.ExternalData.ScreeningMessageResponse)
                 {
                     return(appt);
                 }
             }
             return(null);
         }
         else if (MessageMatches(msg.message, "joined", "ready"))
         {
             foreach (var appt in candidates)
             {
                 if (appt.ExternalData.VideoInviteSent)
                 {
                     return(appt);
                 }
             }
             return(null);
         }
         else if (MessageMatches(msg.message, "arrived"))
         {
             foreach (var appt in candidates)
             {
                 if (appt.ArrivalStatus == AppointmentStatus.Booked)
                 {
                     return(appt);
                 }
             }
             return(null);
         }
         else
         {
             SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_TOO_MANY_APPOINTMENTS, candidates[0], null));
             SmsSender.SendMessage(rmsg);
             LogMsg(OUT, rmsg, "can't choose appointment", appt1);
             UnprocessableMessages.Add(msg);
         }
     }
     return(null);
 }
Пример #4
0
        private void ProcessUnexpectedResponse(PmsAppointment appt, SmsMessage msg)
        {
            SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_UNEXPECTED, appt, null));

            SmsSender.SendMessage(rmsg);
            LogMsg(OUT, rmsg, "unexpected message", appt);
            UnprocessableMessages.Add(msg);
        }
Пример #5
0
        private void HandleUnknownMessage(SmsMessage msg)
        {
            // a future possible enhancement is to ask the user which patient the appointment is for; this will smooth the work flow, but the response
            // processing might be complicated. can it be just a Medicare number and date?
            SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_UNKNOWN_PH, null, null));

            SmsSender.SendMessage(rmsg);
            LogMsg(OUT, rmsg, "handle unknown message", null);
            UnprocessableMessages.Add(msg);
        }
Пример #6
0
 private void ProcessVideoInviteResponse(PmsAppointment appt, SmsMessage msg)
 {
     if (MessageMatches(msg.message, "joined", "ok", "j"))
     {
         // twilio:
         SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_VIDEO_THX, appt, null));
         SmsSender.SendMessage(rmsg);
         LogMsg(OUT, rmsg, "accept video response", appt);
         // PMS:
         appt.ArrivalStatus = AppointmentStatus.Arrived;
         AppointmentUpdater.SaveAppointmentStatusValue(appt);
         // local storage:
         appt.ExternalData.ArrivalStatus = appt.ArrivalStatus;
         Storage.SaveAppointmentStatus(appt);
     }
     else
     {
         // we haven't understood it
         SmsMessage rmsg = new SmsMessage(msg.phone, TemplateProcessor.processTemplate(MessageTemplate.MSG_DONT_UNDERSTAND_VIDEO, appt, null));
         SmsSender.SendMessage(rmsg);
         LogMsg(OUT, rmsg, "fail to process video response", appt);
         UnprocessableMessages.Add(msg);
     }
 }
Пример #7
0
        /// <summary>
        /// This method is called every X minutes to process any changes to the future appointments on the PMS side.
        /// Typically, this covers the next 2 days in the future (not including today, since we don't send the registration message if the appointment is made today)
        ///
        /// </summary>
        /// <param name="stored">The view of the appointments we already had (important, because it remembers what messages we already sent)</param>
        /// <param name="incoming">The current information from the PMS</param>
        public int ProcessUpcomingAppointments(List <PmsAppointment> appointments)
        {
            // pseudo code
            // for each incoming appointment
            //   is it new - send the pre-registration message, and add it to stored
            int t = 0;

            foreach (var appt in appointments.Where(n => IsUseablePhoneNumber(n.PatientMobilePhone) && IsNearFuture(n.AppointmentStartTime) && isNotIgnoreDoctor(n.PractitionerFhirID))) // we only send these messages 2-3 days in the future
            {
                try
                {
                    if (!appt.ExternalData.PostRegistrationMessageSent)
                    {
                        t++;
                        SmsMessage msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_REGISTRATION, appt, null));
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "send registration message", appt);
                        appt.ExternalData.PostRegistrationMessageSent = true;
                        Storage.SaveAppointmentStatus(appt);
                    }
                }
                catch (Exception e)
                {
                    Logger.Log(ERR, "Exception processing " + appt.AppointmentFhirID + ": " + e.Message);
                }
            }
            return(t);
        }
Пример #8
0
        /// <summary>
        /// This method is called every X seconds to process any changes to the appointments on the PMS side
        /// </summary>
        /// <param name="stored">The view of the appointments we already had (important, because it remembers what messages we already sent)</param>
        /// <param name="incoming">The current information from the PMS</param>
        public int ProcessTodaysAppointments(List <PmsAppointment> appointments)
        {
            // pseudo code
            // for each incoming appointment
            //   is it new?- add it to the stored list
            //   has the status changed from arrived to fulfilled? - send the invite message if it's not a TeleHealth consultation
            //   if the appointment is within 3 hours, and the screening message hasn't been sent, send it
            //   if the appointment is within 10 minutes a TeleHealth consultation, and the setup message hasn't been sent, send it
            int t = 0;

            foreach (var appt in appointments.Where(n => IsUseablePhoneNumber(n.PatientMobilePhone) && IsToday(n.AppointmentStartTime) && isNotIgnoreDoctor(n.PractitionerFhirID)))
            {
                try
                {
                    if (appt.ExternalData.ArrivalStatus == AppointmentStatus.Arrived && appt.ArrivalStatus == AppointmentStatus.Fulfilled)
                    {
                        t++;
                        Dictionary <string, string> vars = new Dictionary <string, string>();
                        vars.Add("room", FindRoomNote(appt.PractitionerFhirID));
                        SmsMessage msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_APPT_READY, appt, vars));
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "invite patient to come in", appt);
                        appt.ExternalData.ArrivalStatus = appt.ArrivalStatus;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && IsInTimeWindow(appt.AppointmentStartTime, MinutesBeforeScreening) && !appt.ExternalData.ScreeningMessageSent && !appt.IsVideoConsultation)
                    {
                        t++;
                        SmsMessage msg;
                        if (NoVideoForDoctor(appt.PractitionerFhirID))
                        {
                            msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_SCREENING_NOVIDEO, appt, null));
                            // this one doesn't ask for a yes/no so we say that we have already received the appt response
                            appt.ExternalData.ScreeningMessageResponse = true;
                        }
                        else
                        {
                            msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_SCREENING, appt, null));
                        }
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "send out screening message", appt);
                        appt.ExternalData.ScreeningMessageSent = true;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && IsInTimeWindow(appt.AppointmentStartTime, MinutesBeforeScreening) && !appt.ExternalData.ScreeningMessageSent && appt.IsVideoConsultation)
                    {
                        //t++;
                        // it was made as as telehealth consultation manually
                        // twilio:
                        if (IsDoingVideo)
                        {
                            SmsMessage rmsg = new SmsMessage(appt.PatientMobilePhone, TemplateProcessor.processTemplate(MessageTemplate.MSG_VIDEO_WELCOME, appt, null));
                            SmsSender.SendMessage(rmsg);
                            LogMsg(OUT, rmsg, "start video sequence", appt);

                            // PMS:
                            appt.IsVideoConsultation = true;
                            if (VideoManager.AsksForVideoUrl())
                            {
                                AppointmentUpdater.SaveAppointmentAsVideoMeeting(appt, null, null);
                            }
                            else
                            {
                                var details = VideoManager.getConferenceDetails(appt, false);
                                AppointmentUpdater.SaveAppointmentAsVideoMeeting(appt, "Video URL: " + details, details);
                            }
                        }

                        // local storage
                        appt.ExternalData.ScreeningMessageSent     = true;
                        appt.ExternalData.ScreeningMessageResponse = true;
                        appt.ExternalData.IsVideoConsultation      = true;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && (appt.IsVideoConsultation || appt.ExternalData.IsVideoConsultation) && IsInTimeWindow(appt.AppointmentStartTime, MinutesBeforeVideoInvite) && !appt.ExternalData.VideoInviteSent)
                    {
                        t++;
                        Dictionary <string, string> vars = new Dictionary <string, string>();
                        var details = VideoManager.getConferenceDetails(appt, true);
                        vars.Add("url", details);
                        SmsMessage msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_VIDEO_INVITE, appt, vars));
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "invite to video", appt);
                        appt.ExternalData.VideoInviteSent = true;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && (appt.IsVideoConsultation || appt.ExternalData.IsVideoConsultation) && appt.ExternalData.VideoInviteSent && !String.IsNullOrEmpty(appt.ExternalData.VideoSessionId) && VideoManager.canKnowIfJoined())
                    {
                        if (VideoManager.hasSomeoneJoined(appt.ExternalData.VideoSessionId))
                        {
                            // PMS:
                            appt.ArrivalStatus = AppointmentStatus.Arrived;
                            AppointmentUpdater.SaveAppointmentStatusValue(appt);
                            // Storage
                            appt.ExternalData.ArrivalStatus = appt.ArrivalStatus;
                            Storage.SaveAppointmentStatus(appt);
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.Log(ERR, "Exception processing " + appt.AppointmentFhirID + ": " + e.Message);
                }
            }
            return(t);
        }
Пример #9
0
        /// <summary>
        /// This method is called every X seconds to process any changes to the appointments on the PMS side
        /// </summary>
        /// <param name="stored">The view of the appointments we already had (important, because it remembers what messages we already sent)</param>
        /// <param name="incoming">The current information from the PMS</param>
        public int ProcessTodaysAppointments(List <PmsAppointment> appointments)
        {
            // pseudo code
            // for each incoming appointment
            //   is it new?- add it to the stored list
            //   has the status changed from arrived to fulfilled? - send the invite message if it's not a TeleHealth consultation
            //   if the appointment is within 3 hours, and the screening message hasn't been sent, send it
            //   if the appointment is within 10 minutes a TeleHealth consultation, and the setup message hasn't been sent, send it
            int t = 0;

            foreach (var appt in appointments.Where(n => IsUseablePhoneNumber(n.PatientMobilePhone) && IsToday(n.AppointmentStartTime)))
            {
                try
                {
                    if (appt.ExternalData.ArrivalStatus == AppointmentStatus.Arrived && appt.ArrivalStatus == AppointmentStatus.Fulfilled)
                    {
                        t++;
                        Dictionary <string, string> vars = new Dictionary <string, string>();
                        vars.Add("room", FindRoomNote(appt.PractitionerFhirID));
                        SmsMessage msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_APPT_READY, appt, vars));
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "invite patient to come in", appt);
                        appt.ExternalData.ArrivalStatus = appt.ArrivalStatus;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && IsInTimeWindow(appt.AppointmentStartTime, 180) && !appt.ExternalData.ScreeningMessageSent)
                    {
                        t++;
                        SmsMessage msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_SCREENING, appt, null));
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "send out screening message", appt);
                        appt.ExternalData.ScreeningMessageSent = true;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && (appt.IsVideoConsultation || appt.ExternalData.IsVideoConsultation) && IsInTimeWindow(appt.AppointmentStartTime, VideoManager.getNotificationMinutes()) && !appt.ExternalData.VideoInviteSent)
                    {
                        t++;
                        Dictionary <string, string> vars = new Dictionary <string, string>();
                        var details = VideoManager.getConferenceDetails(appt, true);
                        vars.Add("url", details);
                        SmsMessage msg = new SmsMessage(NormalisePhoneNumber(appt.PatientMobilePhone), TemplateProcessor.processTemplate(MessageTemplate.MSG_VIDEO_INVITE, appt, vars));
                        SmsSender.SendMessage(msg);
                        LogMsg(OUT, msg, "invite to video", appt);
                        appt.ExternalData.VideoInviteSent = true;
                        Storage.SaveAppointmentStatus(appt);
                    }
                    else if (appt.ArrivalStatus == AppointmentStatus.Booked && (appt.IsVideoConsultation || appt.ExternalData.IsVideoConsultation) && appt.ExternalData.VideoInviteSent && !String.IsNullOrEmpty(appt.ExternalData.VideoSessionId) && VideoManager.canKnowIfJoined())
                    {
                        if (VideoManager.hasSomeoneJoined(appt.ExternalData.VideoSessionId))
                        {
                            // PMS:
                            appt.ArrivalStatus = AppointmentStatus.Arrived;
                            AppointmentUpdater.SaveAppointmentStatusValue(appt);
                            // Storage
                            appt.ExternalData.ArrivalStatus = appt.ArrivalStatus;
                            Storage.SaveAppointmentStatus(appt);
                        }
                    }
                }
                catch (Exception e)
                {
                    Logger.Log(ERR, "Exception processing " + appt.AppointmentFhirID + ": " + e.Message);
                }
            }
            return(t);
        }