From 48a651fc63c0bad211a909be6177198dd83ecf2d Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Wed, 23 Dec 2020 13:36:49 -0500 Subject: [PATCH 01/12] Remove assessment from enrollment --- .../patient_mailer/enrollment_email.html.erb | 9 +----- config/locales/en.yml | 31 +++++++++---------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/app/views/patient_mailer/enrollment_email.html.erb b/app/views/patient_mailer/enrollment_email.html.erb index 7e5c09b3cf..cad8b0f21b 100644 --- a/app/views/patient_mailer/enrollment_email.html.erb +++ b/app/views/patient_mailer/enrollment_email.html.erb @@ -9,14 +9,7 @@

<%= t('assessments.email.enrollment.info1', locale: @lang || :en) %>

<%= t('assessments.email.enrollment.info2', locale: @lang || :en) %>

-<%= render partial: 'main_mailer/responsive_button', locals: { - patient: patient, - link: new_patient_assessment_jurisdiction_lang_initials_url(patient[:patient].submission_token, - patient[:jurisdiction_unique_id], - @lang&.to_s || 'en', - patient[:patient]&.initials_age('-')), - text: t('assessments.email.enrollment.report', locale: @lang || :en) - } %>
+ <% end %> <% end %> diff --git a/config/locales/en.yml b/config/locales/en.yml index 5d6f0de2e7..bd4a18cad9 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -97,10 +97,10 @@ en: equal: 'Equal to' not-equal: 'Not Equal to' web: - title: 'Daily Self-Report' + title: 'Follow-up Self-Report' bool-title: 'Please select all symptoms which you are experiencing.' submit: 'Submit' - thank-you: 'Thank You For Completing Your Self Report' + thank-you: 'Thank You For Completing Your Self-Report' instruction1: 'Please continue to follow the recommendations by your health care providers.' instruction2: '' instruction3: 'If you are experiencing a medical emergency, please call 911.' @@ -110,19 +110,18 @@ en: webpage: 'Webpage' email: reminder: - subject: 'VACCS Report Reminder' - header: 'This is your reminder to complete your daily report.' + subject: 'VACCS Follow-up Report Reminder' + header: 'This is your reminder to complete your follow-up report.' dear: 'Dear' - thank-you: 'Thank you for participating in the VACCS monitoring program, please fill out your daily report using the link below.' - report: 'Daily Report' + thank-you: 'Thank you for participating in the VACCS monitoring program, please fill out your follow-up report using the link below.' + report: 'Follow-up Report' footer: 'Do not reply to this email, forward this email, or share this link. This message was automatically generated by the VACCS system and is unique and intended only for you. If you wish to stop receiving these notifications or believe that it was a mistake, please contact us at https://app.vaccs.net/support.' enrollment: subject: 'VACCS Enrollment' header: 'Welcome to the VACCS System!' dear: 'Dear' - info1: 'You have been enrolled in the VACCS monitoring system. We ask that you provide daily reports of your status. Simply click the button below and follow the on-screen instructions.' - info2: 'You will receive a similar reminder daily until your monitoring period has ended. If you have any questions please reach out to the organization that helped enroll you.' - report: 'Daily Report' + info1: 'You have been enrolled in the VACCS monitoring system. We ask that you provide reports of your status following receiving the vaccine. Once the vaccine has been administered, you will receive emails with your assessment linked. ' + info2: 'You will receive daily reminders for one week, followed by a weekly reminder until you receive your second dose. Afterwards, you will receive daily reminders for one week before receiving weekly reminders for six weeks. Following that, you will only receive a reminder at the 6th and 12th month mark. If you have any questions please reach out to the organization that helped enroll you.' footer: 'Do not reply to this email, forward this email, or share this link. This message was automatically generated by the VACCS system and is unique and intended only for you. If you wish to stop receiving these notifications or believe that it was a mistake, please contact us at https://app.vaccs.net/support.' closed: subject: 'VACCS Reporting Complete' @@ -132,22 +131,22 @@ en: footer: 'Do not reply to this email, forward this email, or share this link. This message was automatically generated by the VACCS system and is unique and intended only for you. If you wish to stop receiving these notifications or believe that it was a mistake, please contact us at https://app.vaccs.net/support.' sms: prompt: - intro1: 'Welcome to the VACCS system, we will be sending your daily reports for' + intro1: 'Welcome to the VACCS system, we will be sending your follow-up reports for' intro2: 'to this phone number.' - reminder: 'This is the VACCS system reminding you to please reply to our daily-report messages.' - daily1: 'This is the VACCS daily report for: ' + reminder: 'This is the VACCS system reminding you to please reply to our follow-up report messages.' + daily1: 'This is the VACCS follow-up report for: ' daily2-p: ' Are any of these people ' daily2-s: ' Is this person ' daily3: 'experiencing any of the following symptoms: ' daily4: ' Please reply with "Yes" or "No"' try-again: 'I''m sorry, I didn''t quite get that. Please reply with "Yes" or "No". If you have any questions, please contact your local health department.' - thanks: 'Thank you for completing your daily report!' + thanks: 'Thank you for completing your follow-up report!' weblink: - intro: 'Please complete the VACCS Daily Report for' + intro: 'Please complete the VACCS Follow-up Report for' phone: - intro: 'Hello, this is VACCS, the automated public health assistant calling for your daily report.' + intro: 'Hello, this is VACCS, the automated public health assistant calling for your follow-up report.' try-again: "I'm sorry, I didn't quite get that. Let's try again." - thanks: 'Thank you for completing your daily report! Goodbye.' + thanks: 'Thank you for completing your follow up report! Goodbye.' age: 'Age' daily1: ' This is the report for: ' daily2-p: ' Are any of these people ' From 43eba5caef3dfec91db1c0f2f5431b0f1bbc81bb Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Wed, 23 Dec 2020 13:37:19 -0500 Subject: [PATCH 02/12] Fix eligibility messages and closed assessment email --- .../components/public_health/PatientsTable.js | 2 +- app/jobs/send_appointment_reminders_job.rb | 19 +++ app/models/patient.rb | 146 ++++++++++++------ 3 files changed, 118 insertions(+), 49 deletions(-) create mode 100644 app/jobs/send_appointment_reminders_job.rb diff --git a/app/javascript/components/public_health/PatientsTable.js b/app/javascript/components/public_health/PatientsTable.js index e9f27be99f..ea5fdcce28 100644 --- a/app/javascript/components/public_health/PatientsTable.js +++ b/app/javascript/components/public_health/PatientsTable.js @@ -48,7 +48,7 @@ class PatientsTable extends React.Component { { field: 'end_of_monitoring', label: 'End of Monitoring', isSortable: true, tooltip: null, filter: this.formatDate }, { field: 'latest_report', label: 'Latest Report', isSortable: true, tooltip: null, filter: this.formatTimestamp }, { field: 'status', label: 'Status', isSortable: true, tooltip: null }, - // { field: 'report_eligibility', label: '', isSortable: false, tooltip: null, filter: this.createEligibilityTooltip, icon: 'far fa-comment' }, + { field: 'report_eligibility', label: '', isSortable: false, tooltip: null, filter: this.createEligibilityTooltip, icon: 'far fa-comment' }, ], displayedColData: [], rowData: [], diff --git a/app/jobs/send_appointment_reminders_job.rb b/app/jobs/send_appointment_reminders_job.rb new file mode 100644 index 0000000000..37c982f4c4 --- /dev/null +++ b/app/jobs/send_appointment_reminders_job.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +# SendAssessmentsJob: sends assessment reminder to patients +class SendAppointmentRemindersJob < ApplicationJob + queue_as :mailers + + def perform(*_args) + eligible = Patient.reminder_eligible.count + sent = [] + not_sent = [] + Patient.reminder_eligible.find_each do |patient| + sent << { id: patient.id, method: patient.preferred_contact_method } if patient.send_assessment + rescue StandardError => e + not_sent << { id: patient.id, method: patient.preferred_contact_method, reason: e.message } + next + end + UserMailer.assessment_job_email(sent, not_sent, eligible).deliver_now + end +end diff --git a/app/models/patient.rb b/app/models/patient.rb index 98265184ed..1c27557b02 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -592,7 +592,9 @@ def jurisdiction_path end # Get all dependents (including self if id = responder_id) that are being actively monitored, meaning: - # - not purged AND not closed (monitoring = true) + # - not purged + # AND + # - not closed (monitoring = true) OR if they are closed, it's due to both doses completed (monitoring = false && monitoring_reason = 'Both doses completed') # AND # - in continuous exposure # OR @@ -603,7 +605,7 @@ def jurisdiction_path # - within monitoring period based on creation date if no LDE specified def active_dependents monitoring_days_ago = ADMIN_OPTIONS['monitoring_period_days'].days.ago.beginning_of_day - dependents.where(purged: false, monitoring: true) + dependents.where(purged: false).where('monitoring = ? OR (monitoring = ? && monitoring_reason = ?)', true, false, 'Both doses completed') .where('isolation = ? OR continuous_exposure = ? OR last_date_of_exposure >= ? OR (last_date_of_exposure IS NULL AND created_at >= ?)', true, true, monitoring_days_ago, monitoring_days_ago) end @@ -659,6 +661,30 @@ def send_enrollment_notification end end + # Determine if this is an appropriate day to send + # Daily questionnaire sent daily for 7 days after administration of Dose 1; + # Questionnaires will be sent to Recipient weekly until Dose 2 is administered + # Dose 2 is administered between 21 and 28 days after Dose 1 has been administered. + # Daily questionnaires sent daily for 7 days after dose 2 + # Questionnaire sent Weekly for 6 weeks after daily Dose 2 questionnaires completed. + # Questionnaires will be sent to Recipient once at the 6 and 12 months marks. + def assessment_today? + return false if latest_dosage.nil? + + now_date = Time.now.getlocal(address_timezone_offset).to_date + dose_date = latest_dosage.date_given&.to_date || latest_dosage.created_at.to_date + difference = (now_date - dose_date).to_i + + case latest_dosage&.dose_number + when 1 + (dose_date > (now_date - 7.days).to_date) || now_date.wday == dose_date.wday + when 2 + (dose_date > (now_date - 7.days).to_date) || + (now_date.wday == dose_date.wday && difference / 7 < 7 && (difference / 7).positive?) || + [dose_date + 6.months, dose_date + 1.year].include?(now_date) + end + end + # Send a daily assessment to this monitoree (if currently eligible). By setting send_now to true, an assessment # will be sent immediately without any consideration of the monitoree's preferred_contact_time. def send_assessment(send_now: false) @@ -675,39 +701,15 @@ def send_assessment(send_now: false) # Don't send assessments until the user has at least one dose return if latest_dosage.nil? - # start_of_exposure = last_date_of_exposure || created_at - # return unless (monitoring && start_of_exposure >= ADMIN_OPTIONS['monitoring_period_days'].days.ago.beginning_of_day) || - # (monitoring && isolation) || - # (monitoring && continuous_exposure) || - # active_dependents_exclude_self.exists? + # Don't send assessments if the user is closed with a reason that is not both dosages completed + return if !monitoring && monitoring_reason != 'Both doses completed' # Determine if it is yet an appropriate time to send this person a message. unless send_now # Local "hour" (defaults to eastern if timezone cannot be determined) hour = Time.now.getlocal(address_timezone_offset).hour - now_date = Time.now.getlocal(address_timezone_offset).to_date - dose_date = latest_dosage.date_given&.to_date || latest_dosage.created_at.to_date - difference = (now_date - dose_date).to_i - # Determine if this is an appropriate day to send - # Daily questionnaire sent daily for 7 days after administration of Dose 1; - # Questionnaires will be sent to Recipient weekly until Dose 2 is administered - # Dose 2 is administered between 21 and 28 days after Dose 1 has been administered. - # Daily questionnaires sent daily for 7 days after dose 2 - # Questionnaire sent Weekly for 6 weeks after daily Dose 2 questionnaires completed. - # Questionnaires will be sent to Recipient once at the 6 and 12 months marks. - case latest_dosage&.dose_number - when 1 - return unless (dose_date > (Time.now.getlocal(address_timezone_offset) - 7.days).to_date) || (difference % 7).zero? - when 2 - unless (dose_date > (Time.now.getlocal(address_timezone_offset) - 7.days).to_date) || - ((difference % 7).zero? && difference / 7 < 6 && (difference / 7).positive?) || - (((difference % 30).zero? && (difference / 30 == 6 || difference / 30 == 12))) - return - end - else - return - end + return unless assessment_today? # These are the hours that we consider to be morning, afternoon and evening morning = (8..12) @@ -791,7 +793,7 @@ def select_language # a boolean result to switch on, and a tailored message useful for user interfaces. def report_eligibility report_cutoff_time = Time.now.getlocal('-04:00').beginning_of_day - reporting_period = (ADMIN_OPTIONS['monitoring_period_days'] + 1).days.ago + # reporting_period = (ADMIN_OPTIONS['monitoring_period_days'] + 1).days.ago eligible = true sent = false reported = false @@ -814,7 +816,8 @@ def report_eligibility end # Can't send messages to monitorees that are on the closed line list and have no active dependents. - if !monitoring && active_dependents.empty? + # TODO: For now, we will allow notifications to those who has a reason of "Both doses completed" + if !monitoring && active_dependents.empty? && monitoring_reason != 'Both doses completed' eligible = false # If this person has dependents (is a HoH) @@ -836,15 +839,16 @@ def report_eligibility end # Exposure workflow specific conditions - unless isolation - # Monitoring period has elapsed - start_of_exposure = last_date_of_exposure || created_at - no_active_dependents = !active_dependents_exclude_self.exists? - if start_of_exposure < reporting_period && !continuous_exposure && no_active_dependents - eligible = false - messages << { message: "Recipient\'s monitoring period has elapsed and continuous exposure is not enabled", datetime: nil } - end - end + # TODO: Removing this at the moment as we don't have concept of isolation + # unless isolation + # # Monitoring period has elapsed + # start_of_exposure = last_date_of_exposure || created_at + # no_active_dependents = !active_dependents_exclude_self.exists? + # if start_of_exposure < reporting_period && !continuous_exposure && no_active_dependents + # eligible = false + # messages << { message: "Recipient\'s monitoring period has elapsed and continuous exposure is not enabled", datetime: nil } + # end + # end # Has already been contacted today if !last_assessment_reminder_sent.nil? && last_assessment_reminder_sent >= 12.hours.ago @@ -860,23 +864,69 @@ def report_eligibility messages << { message: 'Recipient has already reported today', datetime: latest_assessment_at } end + # Hasn't received the vaccine + if latest_dosage.nil? + eligible = false + messages << { message: 'Recipient has not received a vaccine dosage yet' } + end + + # Received Dose 2 over a year ago + if !latest_dosage.nil? && latest_dosage.dose_number == 2 && latest_dosage.date_given < 1.year.ago + eligible = false + messages << { message: 'Recipient has completed all of their assessments' } + end + # Rough estimate of next contact time if eligible - messages << case preferred_contact_time - when 'Morning' - { message: '8:00 AM local time (Morning)', datetime: nil } - when 'Afternoon' - { message: '12:00 PM local time (Afternoon)', datetime: nil } - when 'Evening' - { message: '4:00 PM local time (Evening)', datetime: nil } + # Are they going to be contacted today? + messages << if assessment_today? + case preferred_contact_time + when 'Morning' + { message: '8:00 AM local time (Morning)', datetime: nil } + when 'Afternoon' + { message: '12:00 PM local time (Afternoon)', datetime: nil } + when 'Evening' + { message: '4:00 PM local time (Evening)', datetime: nil } + else + { message: 'Today', datetime: nil } + end + # Okay, not today -- figure out what is the next date for them to be contacted else - { message: 'Today', datetime: nil } + { message: 'Recipient will be contacted on ', datetime: next_assessment_date } end end { eligible: eligible, sent: sent, reported: reported, messages: messages, household: household } end + # When is the next time the patient should receive an assessment? + def next_assessment_date + return if latest_dosage.nil? + + now_date = Time.now.getlocal(address_timezone_offset).to_date + dose_date = latest_dosage.date_given&.to_date || latest_dosage.created_at.to_date + + # If it's today, just return today + return now_date.to_date if assessment_today? + + # If it's dose 1, then we're always counting on weekly reminders + # If it's dose 2, then we're going to check if it's within the first 6 weeks + if latest_dosage.dose_number == 1 || latest_dosage.dose_number == 2 && now_date < dose_date + 6 * 7 + day_of_week_dose = dose_date.wday + day_of_week_now = now_date.wday + + # Normalize the difference + day_difference = (day_of_week_dose - day_of_week_now + 7) % 7 + (now_date + day_difference).to_date + # If we're dose 2 but not within the first 6 weeks, we are waiting for the 6th month/12th month check in + elsif latest_dosage.dose_number == 2 && now_date < dose_date + 1.year + six_month = dose_date + 6.months + return six_month.to_date if (six_month - now_date).to_i.positive? + + (dose_date + 1.year).to_date + end + end + # Returns a representative FHIR::Patient for an instance of a Sara Alert Patient. Uses US Core # extensions for sex, race, and ethnicity. # https://www.hl7.org/fhir/us/core/StructureDefinition-us-core-patient.html From e0214fe38d1345817ac76596938d3a743b173d47 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 4 Jan 2021 12:11:00 -0500 Subject: [PATCH 03/12] Format date --- app/javascript/components/util/EligibilityTooltip.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/javascript/components/util/EligibilityTooltip.js b/app/javascript/components/util/EligibilityTooltip.js index 5b45f6131a..646ae50c36 100644 --- a/app/javascript/components/util/EligibilityTooltip.js +++ b/app/javascript/components/util/EligibilityTooltip.js @@ -13,6 +13,10 @@ class EligibilityTooltip extends React.Component { return ts.isValid() ? ts.tz(moment.tz.guess()).format('MM/DD/YYYY HH:mm z') : ''; } + formatDate(date) { + return date ? moment(date, 'YYYY-MM-DD').format('MM/DD/YYYY') : ''; + } + formatEligibility(eligibility, id) { if (eligibility.household) { return ( @@ -47,7 +51,9 @@ class EligibilityTooltip extends React.Component {

Eligible for notifications:

- {eligibility.messages[0].message} + + {eligibility.messages[0].message} {eligibility.messages[0].datetime ? ` ${this.formatDate(eligibility.messages[0].datetime)}` : ''} +
From 5ad347d5663250c110d280336210df07aa3dfff5 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 11 Jan 2021 12:12:51 -0500 Subject: [PATCH 04/12] Fix overflow on Dosage table --- app/views/patients/show.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/patients/show.html.erb b/app/views/patients/show.html.erb index eb81d41578..dc18fce672 100644 --- a/app/views/patients/show.html.erb +++ b/app/views/patients/show.html.erb @@ -271,7 +271,7 @@ "oLanguage": { "sSearch": "Search Dosages:" }, - "dom": "<'row'<'col-sm-24 col-md-12'l><'col-sm-24 col-md-12'f>>" + "<'row'<'col-sm-24'tr>>" + "<'row'<'col-sm-24 col-md-10'i><'col-sm-24 col-md-14'p>>" + "dom": "<'row'<'col-sm-24 col-md-12'l><'col-sm-24 col-md-12'f>>" + "<'row'<'table-responsive'<'col-sm-24'tr>>>" + "<'row'<'col-sm-24 col-md-10'i><'col-sm-24 col-md-14'p>>" }); $('.cc_table').DataTable({ "search": false, From 89a5fb6a7c7667b55536ef6d74d4682c0bc287e8 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 11 Jan 2021 12:17:31 -0500 Subject: [PATCH 05/12] Change enrollment for SMS texts --- app/mailers/patient_mailer.rb | 20 ++++++++++++-------- config/locales/en.yml | 6 ++++++ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/app/mailers/patient_mailer.rb b/app/mailers/patient_mailer.rb index 3560186836..24bc82d06b 100644 --- a/app/mailers/patient_mailer.rb +++ b/app/mailers/patient_mailer.rb @@ -2,6 +2,7 @@ # PatientMailer: mailers for monitorees class PatientMailer < ApplicationMailer + # Inform the patient via email that they will receive notifications after vaccine. def enrollment_email(patient) return if patient&.email.blank? @@ -17,15 +18,17 @@ def enrollment_email(patient) History.welcome_message_sent(patient: patient) end + # Inform the patient via SMS that they will receive notifications after vaccine. def enrollment_sms_weblink(patient) return if patient&.primary_telephone.blank? - lang = patient.select_language - url = new_patient_assessment_jurisdiction_lang_initials_url(patient.submission_token, - patient.jurisdiction.unique_identifier, - lang&.to_s || 'en', - patient&.initials_age) - contents = "#{I18n.t('assessments.sms.weblink.intro', locale: lang)} #{patient&.initials_age('-')}: #{url}" + lang = patient.select_language || 'en' + # url = new_patient_assessment_jurisdiction_lang_initials_url(patient.submission_token, + # patient.jurisdiction.unique_identifier, + # lang&.to_s || 'en', + # patient&.initials_age) + # contents = "#{I18n.t('assessments.sms.weblink.intro', locale: lang)} #{patient&.initials_age('-')}: #{url}" + contents = "#{I18n.t('assessments.sms.enrollment.info1', locale: lang)} #{I18n.t('assessments.sms.enrollment.weblink.info2', locale: lang)}" account_sid = ENV['TWILLIO_API_ACCOUNT'] auth_token = ENV['TWILLIO_API_KEY'] from = ENV['TWILLIO_SENDING_NUMBER'] @@ -41,11 +44,12 @@ def enrollment_sms_weblink(patient) patient.update(last_assessment_reminder_sent: DateTime.now) end + # Inform the user via SMS that they will receive notifications after vaccine. def enrollment_sms_text_based(patient) return if patient&.primary_telephone.blank? - lang = patient.select_language - contents = "#{I18n.t('assessments.sms.prompt.intro1', locale: lang)} #{patient&.initials_age('-')} #{I18n.t('assessments.sms.prompt.intro2', locale: lang)}" + lang = patient.select_language || 'en' + contents = "#{I18n.t('assessments.sms.enrollment.info1', locale: lang)} #{I18n.t('assessments.sms.enrollment.prompt.info2', locale: lang)}" account_sid = ENV['TWILLIO_API_ACCOUNT'] auth_token = ENV['TWILLIO_API_KEY'] from = ENV['TWILLIO_SENDING_NUMBER'] diff --git a/config/locales/en.yml b/config/locales/en.yml index bd4a18cad9..4c3184b6dc 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -130,6 +130,12 @@ en: thank-you: 'Your VACCS monitoring is now complete! Thank you for your participation.' footer: 'Do not reply to this email, forward this email, or share this link. This message was automatically generated by the VACCS system and is unique and intended only for you. If you wish to stop receiving these notifications or believe that it was a mistake, please contact us at https://app.vaccs.net/support.' sms: + enrollment: + info1: 'You have been enrolled in the VACCS monitoring system. We ask that you provide reports of your status following receiving the vaccine.' + prompt: + info2: 'Once the vaccine has been administered, you will receive follow-up reports at this number.' + weblink: + info2: 'Once the vaccine has been administered, you will receive a message at this number with your assessment linked.' prompt: intro1: 'Welcome to the VACCS system, we will be sending your follow-up reports for' intro2: 'to this phone number.' From 850aa03190361973e483fd7ca883a8a5fb8ae7ce Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 11 Jan 2021 15:52:39 -0500 Subject: [PATCH 06/12] Remove 'Both Doses Completed' as closure reason --- app/javascript/components/public_health/actions/CloseRecords.js | 2 +- .../tests/subject/monitoring_actions/MonitoringStatus.test.js | 1 - app/models/patient.rb | 1 - 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/app/javascript/components/public_health/actions/CloseRecords.js b/app/javascript/components/public_health/actions/CloseRecords.js index 045d6b0692..48ad8d9d97 100644 --- a/app/javascript/components/public_health/actions/CloseRecords.js +++ b/app/javascript/components/public_health/actions/CloseRecords.js @@ -13,7 +13,7 @@ class CloseRecords extends React.Component { apply_to_household: false, loading: false, monitoring: false, - monitoring_reasons: ['Both doses completed', 'Deceased', 'Duplicate', 'Person under investigation', 'Other'], + monitoring_reasons: ['Deceased', 'Duplicate', 'Person under investigation', 'Other'], monitoring_reason: '', reasoning: '', }; diff --git a/app/javascript/tests/subject/monitoring_actions/MonitoringStatus.test.js b/app/javascript/tests/subject/monitoring_actions/MonitoringStatus.test.js index 823acc1832..d377415575 100644 --- a/app/javascript/tests/subject/monitoring_actions/MonitoringStatus.test.js +++ b/app/javascript/tests/subject/monitoring_actions/MonitoringStatus.test.js @@ -13,7 +13,6 @@ const authyToken = 'Q1z4yZXLdN+tZod6dBSIlMbZ3yWAUFdY44U06QWffEP76nx1WGMHIz8rYxEU const monitoringStatusValues = [ 'Actively Monitoring', 'Not Monitoring' ]; const monitoringReasons = [ '--', - 'Both doses completed', 'Deceased', 'Duplicate', 'Person under investigation', diff --git a/app/models/patient.rb b/app/models/patient.rb index 1c27557b02..8cb7d57b5c 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -34,7 +34,6 @@ class Patient < ApplicationRecord 'Deceased', 'Duplicate', 'Other', - 'Both doses completed', 'Person under investigation', nil, ''] } From 55d1fea207d415001b9f4052c1dad06ab0a9b4e7 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 11 Jan 2021 16:06:21 -0500 Subject: [PATCH 07/12] Removed irrelevant file --- app/jobs/send_appointment_reminders_job.rb | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 app/jobs/send_appointment_reminders_job.rb diff --git a/app/jobs/send_appointment_reminders_job.rb b/app/jobs/send_appointment_reminders_job.rb deleted file mode 100644 index 37c982f4c4..0000000000 --- a/app/jobs/send_appointment_reminders_job.rb +++ /dev/null @@ -1,19 +0,0 @@ -# frozen_string_literal: true - -# SendAssessmentsJob: sends assessment reminder to patients -class SendAppointmentRemindersJob < ApplicationJob - queue_as :mailers - - def perform(*_args) - eligible = Patient.reminder_eligible.count - sent = [] - not_sent = [] - Patient.reminder_eligible.find_each do |patient| - sent << { id: patient.id, method: patient.preferred_contact_method } if patient.send_assessment - rescue StandardError => e - not_sent << { id: patient.id, method: patient.preferred_contact_method, reason: e.message } - next - end - UserMailer.assessment_job_email(sent, not_sent, eligible).deliver_now - end -end From 40145e8da1db4e8d1534f48da37f0eea4b11c6e0 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 11 Jan 2021 16:07:49 -0500 Subject: [PATCH 08/12] Removed comments --- app/mailers/patient_mailer.rb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/mailers/patient_mailer.rb b/app/mailers/patient_mailer.rb index 24bc82d06b..543edd89be 100644 --- a/app/mailers/patient_mailer.rb +++ b/app/mailers/patient_mailer.rb @@ -23,11 +23,6 @@ def enrollment_sms_weblink(patient) return if patient&.primary_telephone.blank? lang = patient.select_language || 'en' - # url = new_patient_assessment_jurisdiction_lang_initials_url(patient.submission_token, - # patient.jurisdiction.unique_identifier, - # lang&.to_s || 'en', - # patient&.initials_age) - # contents = "#{I18n.t('assessments.sms.weblink.intro', locale: lang)} #{patient&.initials_age('-')}: #{url}" contents = "#{I18n.t('assessments.sms.enrollment.info1', locale: lang)} #{I18n.t('assessments.sms.enrollment.weblink.info2', locale: lang)}" account_sid = ENV['TWILLIO_API_ACCOUNT'] auth_token = ENV['TWILLIO_API_KEY'] From a72c6ffa3d1d4420e6f194db2919a3fd7a1b28b2 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Mon, 11 Jan 2021 16:09:02 -0500 Subject: [PATCH 09/12] Fixed typo --- config/locales/en.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/en.yml b/config/locales/en.yml index 4c3184b6dc..8250038491 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -152,7 +152,7 @@ en: phone: intro: 'Hello, this is VACCS, the automated public health assistant calling for your follow-up report.' try-again: "I'm sorry, I didn't quite get that. Let's try again." - thanks: 'Thank you for completing your follow up report! Goodbye.' + thanks: 'Thank you for completing your follow-up report! Goodbye.' age: 'Age' daily1: ' This is the report for: ' daily2-p: ' Are any of these people ' From e1ac6596b79b7daeb0728fc1783b9c49b02ee036 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Thu, 14 Jan 2021 13:55:40 -0500 Subject: [PATCH 10/12] Add registered tab --- app/controllers/public_health_controller.rb | 27 +++++++++++-------- app/helpers/patient_details_helper.rb | 1 + .../components/subject/CurrentStatus.js | 2 ++ app/javascript/components/util/InfoTooltip.js | 10 ++++--- app/models/patient.rb | 19 +++++++++++++ app/views/patients/_patients.html.erb | 12 +-------- app/views/public_health/exposure.html.erb | 2 ++ 7 files changed, 47 insertions(+), 26 deletions(-) diff --git a/app/controllers/public_health_controller.rb b/app/controllers/public_health_controller.rb index ae4a8d6851..8f649ea503 100644 --- a/app/controllers/public_health_controller.rb +++ b/app/controllers/public_health_controller.rb @@ -15,7 +15,7 @@ def patients # Validate tab param tab = permitted_params.require(:tab).to_sym if workflow == :exposure - return head :bad_request unless %i[all followup symptomatic non_reporting asymptomatic pui closed transferred_in transferred_out].include?(tab) + return head :bad_request unless %i[all followup symptomatic non_reporting asymptomatic registered pui closed transferred_in transferred_out].include?(tab) else return head :bad_request unless %i[all requiring_review non_reporting reporting closed transferred_in transferred_out].include?(tab) end @@ -105,7 +105,7 @@ def tab_counts # Validate tab param tab = params.require(:tab).to_sym if workflow == :exposure - return head :bad_request unless %i[all followup symptomatic non_reporting asymptomatic pui closed transferred_in transferred_out].include?(tab) + return head :bad_request unless %i[all followup symptomatic non_reporting asymptomatic registered pui closed transferred_in transferred_out].include?(tab) else return head :bad_request unless %i[all requiring_review non_reporting reporting closed transferred_in transferred_out].include?(tab) end @@ -129,6 +129,7 @@ def patients_by_type(workflow, tab) return current_user.viewable_patients.exposure_symptomatic if tab == :symptomatic return current_user.viewable_patients.exposure_non_reporting if tab == :non_reporting return current_user.viewable_patients.exposure_asymptomatic if tab == :asymptomatic + return current_user.viewable_patients.exposure_registered if tab == :registered return current_user.viewable_patients.exposure_under_investigation if tab == :pui else return current_user.viewable_patients.isolation_requiring_review if tab == :requiring_review @@ -200,11 +201,14 @@ def sort(patients, order, direction) when 'latest_report' patients = patients.order('CASE WHEN latest_assessment_at IS NULL THEN 1 ELSE 0 END, latest_assessment_at ' + dir) when 'status' - # For alphabetical sorting: closed 0, followup 1, non-reporting 2, reviewed 3, symptomatic 4 - # Case determination -> closed, followup, symptomatic, reviewed, non-reporting - patients = patients.order('CASE WHEN monitoring != true THEN 0 WHEN severe_symptom_onset IS NOT NULL THEN 1 WHEN symptom_onset IS NOT NULL then 4 WHEN - latest_assessment_at IS NOT NULL and latest_assessment_at >= DATE_SUB(DATE(NOW()), INTERVAL ' + - reporting_period + ' MINUTE) THEN 3 ELSE 2 END ' + dir) + # For alphabetical sorting: closed 0, followup 1, non-reporting 2, registered 3, reviewed 4, symptomatic 5 + # Case determination -> closed, followup, symptomatic, registered, reviewed, non-reporting + patients = patients.select('patients.*, MAX(dosages.date_given)').joins('LEFT JOIN dosages ON patients.id = dosages.patient_id') + .group('patients.id') + .order('CASE WHEN monitoring != true THEN 0 WHEN MAX(dosages.date_given) IS NULL THEN 3 WHEN severe_symptom_onset IS NOT NULL THEN 1 + WHEN symptom_onset IS NOT NULL then 5 WHEN latest_assessment_at IS NOT NULL and latest_assessment_at >= DATE_SUB(UTC_TIMESTAMP() + , INTERVAL ' + reporting_period + ' MINUTE) THEN 4 WHEN patients.created_at is NOT NULL and patients.created_at >= + DATE_SUB(UTC_TIMESTAMP(), INTERVAL ' + reporting_period + ' MINUTE) THEN 4 ELSE 2 END ' + dir) when 'dose1_date' patients = patients.select('patients.*, dosages.dose_number, MAX(dosages.date_given)').joins('LEFT JOIN dosages ON patients.id = dosages.patient_id') .group('patients.id, dosages.dose_number').having('dosages.dose_number = 1 OR dosages.dose_number IS NULL') @@ -282,18 +286,19 @@ def linelist(patients, workflow, tab) end def linelist_specific_fields(workflow, tab) - return %i[jurisdiction assigned_user expected_purge_date reason_for_closure closed_at dose1_date dose2_date dose2_app] if tab == :closed + return %i[jurisdiction expected_purge_date reason_for_closure closed_at dose1_date dose2_date dose2_app] if tab == :closed if workflow == :isolation - return %i[jurisdiction assigned_user extended_isolation symptom_onset monitoring_plan latest_report status report_eligibility] if tab == :all + return %i[jurisdiction extended_isolation symptom_onset monitoring_plan latest_report status report_eligibility] if tab == :all return %i[transferred_from monitoring_plan transferred_at] if tab == :transferred_in return %i[transferred_to monitoring_plan transferred_at] if tab == :transferred_out - return %i[jurisdiction assigned_user extended_isolation symptom_onset monitoring_plan latest_report report_eligibility] + return %i[jurisdiction extended_isolation symptom_onset monitoring_plan latest_report report_eligibility] end return %i[jurisdiction end_of_monitoring latest_report status report_eligibility dose1_date dose2_date dose2_app] if tab == :all - return %i[jurisdiction assigned_user end_of_monitoring risk_level public_health_action latest_report report_eligibility] if tab == :pui + return %i[jurisdiction] if tab == :registered + return %i[jurisdiction end_of_monitoring risk_level public_health_action latest_report report_eligibility] if tab == :pui return %i[transferred_from end_of_monitoring risk_level monitoring_plan transferred_at] if tab == :transferred_in return %i[transferred_to end_of_monitoring risk_level monitoring_plan transferred_at] if tab == :transferred_out diff --git a/app/helpers/patient_details_helper.rb b/app/helpers/patient_details_helper.rb index acd5cd7d16..5a1d070f0d 100644 --- a/app/helpers/patient_details_helper.rb +++ b/app/helpers/patient_details_helper.rb @@ -8,6 +8,7 @@ def status return :closed unless monitoring unless isolation + return :exposure_registered if latest_dosage.nil? return :exposure_follow_up unless severe_symptom_onset.nil? return :exposure_under_investigation if public_health_action != 'None' return :exposure_symptomatic unless symptom_onset.nil? diff --git a/app/javascript/components/subject/CurrentStatus.js b/app/javascript/components/subject/CurrentStatus.js index 4392a70ced..cb70659e44 100644 --- a/app/javascript/components/subject/CurrentStatus.js +++ b/app/javascript/components/subject/CurrentStatus.js @@ -16,6 +16,8 @@ class CurrentStatus extends React.Component { return needs follow up; } else if (status === 'exposure_reviewed') { return reviewed; + } else if (status === 'exposure_registered') { + return registered; } else if (status === 'exposure_non_reporting') { return non-reporting; } else if (status === 'exposure_under_investigation') { diff --git a/app/javascript/components/util/InfoTooltip.js b/app/javascript/components/util/InfoTooltip.js index 6b8857a61d..c835c025d9 100644 --- a/app/javascript/components/util/InfoTooltip.js +++ b/app/javascript/components/util/InfoTooltip.js @@ -261,15 +261,15 @@ const TOOLTIP_TEXT = { // EXPOSURE WORKFLOW LINE LIST DEFINITIONS exposure_symptomatic: (
- Recipients on this list require public health follow-up to determine if disease is suspected. Follow-up should be based on current guidelines and - available resources. + Recipients on this list are experiencing symptoms following receiving the vaccine. Follow-up should be based on current guidelines and available + resources.
), exposure_followup: (
- Recipients on this list require public health follow-up to determine if disease is suspected. Follow-up should be based on current guidelines and - available resources. + Recipients on this list are experiencing severe adverse reactions following receiving the vaccine and require public health follow-up. Follow-up should be + based on current guidelines and available resources.
), @@ -282,6 +282,8 @@ const TOOLTIP_TEXT = { exposure_asymptomatic:
Recipients on this list do not require public health follow-up unless otherwise indicated.
, + exposure_registered:
Recipients on this list have not received a vaccine dosage yet.
, + exposure_under_investigation: (
A ‘Latest Public Health Action’ other than “None” has been documented in the recipient’s record. Recipients on this list do not receive daily reminder diff --git a/app/models/patient.rb b/app/models/patient.rb index 8cb7d57b5c..ff5edffd28 100644 --- a/app/models/patient.rb +++ b/app/models/patient.rb @@ -238,6 +238,7 @@ def latest_dose1 # Any individual needing a follow up scope :followup, lambda { where(monitoring: true) + .joins(:dosages) .where(purged: false) .where(public_health_action: 'None') .where.not(severe_symptom_onset: nil) @@ -247,6 +248,7 @@ def latest_dose1 # Any individual who has any assessments still considered symptomatic (includes patients in both exposure & isolation workflows) scope :symptomatic, lambda { where(monitoring: true) + .joins(:dosages) .where(purged: false) .where(public_health_action: 'None') .where(severe_symptom_onset: nil) @@ -257,12 +259,14 @@ def latest_dose1 # Individuals who have reported recently and are not symptomatic (includes patients in both exposure & isolation workflows) scope :asymptomatic, lambda { where(monitoring: true) + .joins(:dosages) .where(purged: false) .where(public_health_action: 'None') .where(symptom_onset: nil) .where('latest_assessment_at >= ?', ADMIN_OPTIONS['reporting_period_minutes'].minutes.ago) .or( where(monitoring: true) + .joins(:dosages) .where(purged: false) .where(public_health_action: 'None') .where(symptom_onset: nil) @@ -274,6 +278,7 @@ def latest_dose1 # Non reporting asymptomatic individuals (includes patients in both exposure & isolation workflows) scope :non_reporting, lambda { where(monitoring: true) + .joins(:dosages) .where(purged: false) .where(public_health_action: 'None') .where(symptom_onset: nil) @@ -281,6 +286,7 @@ def latest_dose1 .where('patients.created_at < ?', ADMIN_OPTIONS['reporting_period_minutes'].minutes.ago) .or( where(monitoring: true) + .joins(:dosages) .where(purged: false) .where(public_health_action: 'None') .where(symptom_onset: nil) @@ -290,6 +296,15 @@ def latest_dose1 .distinct } + # Patients who haven't received a vaccine + scope :registered, lambda { + where(purged: false) + .where(monitoring: true) + .joins('LEFT JOIN dosages ON patients.id = dosages.patient_id') + .where('dosages.id IS NULL') + .distinct + } + # Any individual who is currently under investigation (exposure workflow only) scope :exposure_under_investigation, lambda { where(isolation: false) @@ -319,6 +334,10 @@ def latest_dose1 where(isolation: false).asymptomatic.distinct } + scope :exposure_registered, lambda { + where(isolation: false).registered.distinct + } + # Individuals that meet the asymptomatic recovery definition (isolation workflow only) scope :isolation_asymp_non_test_based, lambda { where(monitoring: true) diff --git a/app/views/patients/_patients.html.erb b/app/views/patients/_patients.html.erb index f863ffbd93..58e546d1b0 100644 --- a/app/views/patients/_patients.html.erb +++ b/app/views/patients/_patients.html.erb @@ -12,22 +12,13 @@
-
- -
+ - @@ -41,7 +32,6 @@ - diff --git a/app/views/public_health/exposure.html.erb b/app/views/public_health/exposure.html.erb index c0d6bbf47e..d93afcf7ec 100644 --- a/app/views/public_health/exposure.html.erb +++ b/app/views/public_health/exposure.html.erb @@ -32,6 +32,8 @@ description: 'Recipients who have failed to report in the last day, and are not symptomatic.' }, asymptomatic: { label: 'Reviewed', variant: 'success', tooltip: 'exposure_asymptomatic', description: 'Recipients currently reporting no symptoms who have reported during the last day, or patients with symptoms that have already been reviewed.' }, + registered: { label: 'Registered', variant: 'secondary', tooltip: 'exposure_registered', + description: 'Recipients who have not received a vaccine dosage.' }, closed: { label: 'Closed', variant: 'secondary', tooltip: 'exposure_closed', description: 'Recipients not currently being monitored.' }, all: { label: 'All Recipients', variant: 'primary', From 68db8912a3a9c0bd692055ee133b436c91236421 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Tue, 19 Jan 2021 11:14:23 -0500 Subject: [PATCH 11/12] Upgrade version number --- config/sara/sara.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/sara/sara.yml b/config/sara/sara.yml index c50f2334aa..4447b7e952 100644 --- a/config/sara/sara.yml +++ b/config/sara/sara.yml @@ -1,5 +1,5 @@ # Version -version: v1.0.0 +version: v1.0.2 # Base path (useful for WARP or similar environments) base_path: '' From 83670328d6f427b5b585cfc39d16d2214ea40624 Mon Sep 17 00:00:00 2001 From: Christine Duong Date: Tue, 19 Jan 2021 11:15:12 -0500 Subject: [PATCH 12/12] Upgrade version number --- config/sara/sara.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/sara/sara.yml b/config/sara/sara.yml index c50f2334aa..7b886ea36d 100644 --- a/config/sara/sara.yml +++ b/config/sara/sara.yml @@ -1,5 +1,5 @@ # Version -version: v1.0.0 +version: v1.0.3 # Base path (useful for WARP or similar environments) base_path: ''
Recipient JurisdictionAssigned User State/Local ID Sex Date of Birth
<%= link_to "#{patient.last_name}, #{patient.first_name}", patient, class: '' %> <%= patient.jurisdiction.name %><%= patient.assigned_user %> <%= patient.user_defined_id_statelocal.blank? ? '' : patient.user_defined_id_statelocal %> <%= patient.sex %> <%= patient.date_of_birth&.strftime('%m/%d/%Y') %>