Skip to content
This repository has been archived by the owner on Sep 9, 2024. It is now read-only.

Admin Messages

Anonymous edited this page Sep 22, 2022 · 20 revisions

Messages

Table of contents

Introduction

The messages module is used to send messages (calls, sms, WhatsApp) to the patient.

This module can be found here.

Scheduled messages

Patient and caregiver relation

The messages module allows scheduling messages for patients and caregivers. The application administrator determines if the caregiver functionality is available for the particular Connect for Life™ implementation.

The caregiver is an OpenMRS relation, configured as usable in this context through OpenMRS properties. Implementations can configure other, custom relations to be included as potential message targets (for example doctors). A caregiver is registered as a patient’s relative. Although multiple relations of a single patient can be added in OpenMRS, we will use the term caregiver to refer to these relations throughout the documentation.

Managing relationship types can be accessed from the "Advanced Administration" page.

Each caregiver can be related to numerous patients (eg. a mother can be the caregiver for multiple children) and can have different messages schedules for every related patient. The purpose of registering a caregiver is to be able to reach not only the patients, but also people responsible for them (patients can be incapable of taking good care of themselves in multiple scenarios).

Default message templates

The settings are slightly different for every message type. It is the message template that dictates what input fields are available in a particular message type, and how they affect the messages that are sent to the patient.

A message template is a set of parameters suitable for a message type, like frequency, start date of messages, end date of messages, call or SMS, etc. It can be added, edited and deleted by injecting proper SQL. Templates are customizable at the implementation level.

Setting default templates feature is only accessible by the application administrator. It allows to set default values for best contact time and message settings for patients and caregivers – excluding the start date of messages. Options set on this page are the default settings for the message schedule of a new patient.

Default settings are set for the particular implementation.

Go to the "Messages" page via "System Administration" - Messages.

"Messages" page includes:

  • Information about the time zone (UTC)
  • Best contact time for patients, caregivers and other relations (time picker)
  • Tabs with available messages types where you set the global defaults same as at the patient level

One difference between messages settings in the application administrator view and the user view is the form of the “Other” option in the "End date" of sending messages section.

In the application administration view, the dropdown with time options is available. In the user view, this period is calculated to the exact date, counted from the registration date.

Example:

According to the default settings set by the application administrator for adherence report daily, this message type should end by default after 1 month. If a patient is registered on May the 1st and messages are activated with the default settings, the last day of sending the adherence report daily is June the 1st.

Note: The dropdown content for the "End of messages" is not customizable.

Time zone

The time zone is depicted in a 24hour format and is customizable by the “messages.defaultUserTimezone” global OpenMRS property. This information is displayed below the patient and caregiver header on the "Calendar Overview" page, "Manage Messages" page and "Messages Settings" page.

Flow and content of messages

Message flows are sets of messages types combined into ordered strings.

Composing phone call flows takes part through the call flows module and is described in the Call flows designer section of the Call Flow module documentation.

Composing SMS flows is available through global properties. All of the global properties presented below are customizable.

The following bulleted list presents the list of Global Properties used by default by the "Messages" module.

  • messages.notificationTemplate.injectedServices

Description:

List of Spring beans which can be used in the SMS Velocity template in order to create an SMS content. These beans can be used to retrieve information included in SMS messages, for example, patient’s name, etc. These services can then be used by referring to them by their name.

Property format:

"<name>:<bean name>,...”

Default value:

patientService:patientService,cflPersonService:cflPersonService,messagesService:messages.messagingService,personService:personService,personDAO:personDAO,conceptDAO:conceptDAO,locationService:locationService,messagingGroupService:messages.messagingGroupService,openmrsContext:context,patientTemplateService:messages.patientTemplateService

Example of using:

Assume that this is the value of the global property: “patSer:patientService”, where “patSer” is the user-friendly name which can be used in the velocity template and “patientService” is the ID of the Spring Bean component.

The service defined in that way can be used in the velocity template. For instance:

Hello patient with ID 7, your age is $patSer.getPatient(7).getAge()

Note: The above can be replaced with:

Hello patient this message is sent to, your age is $patient.getAge()

This is because the variable $patientid is always set to the patient being targeted in the message.

  • messages.notificationTemplate.adherence-feedback

Description:

Responsible for presenting calculated adherence feedback. This property takes a velocity templates as the value. The output of that template will be used as a notification message.

Property format:

Velocity template

Default value:

#set($adfLevel= $ADHERENCE_LEVEL)
#set($adfTrend= $ADHERENCE_TREND)
#set($textToRead= "")
#if($patient.getId().equals($actor.getId()))
	#set($textToRead= "Your adherence for past week is $adfLeveland compare to last week your adherence trend is $adfTrend")
#else
	#set($textToRead= "Your patient's adherence for past week is $adfLeveland compare to last week your patient's adherence trend is $adfTrend")
#end
$textToRead
  • messages.notificationTemplate.adherence-report-daily

Description:

Responsible for the daily pill reminder message. This property takes a velocity templates as the value. The output of that template will be used as a notification message.

Property format:

Velocity template

Default value:

#set($textToRead= "")
#if($patient.getId().equals($actor.getId())) 
	#set($textToRead= "Hello $patient.getPersonName().toString(), Your dosage is scheduled to be taken now. Please take your dosage.")
#else
	#set($textToRead= "Hello $actor.getPersonName().toString(), Your patient's dosage is scheduled to be taken now. ")
#end
$textToRead
  • messages.notificationTemplate.adherence-report-weekly

Description:

Responsible for weekly pill reminder message. This property takes a velocity templates as the value. The output of that template will be used as anotification message.

Property format:

Velocity template

Default value:

#set($textToRead= "")
#if($patient.getId().equals($actor.getId()))
	#set($textToRead= "Hello $patient.getPersonName().toString(), Your dosage is scheduled to be taken. Please take your dosage as prescribed.")
#else
	#set($textToRead= "Hello $actor.getPersonName().toString(), Your patient's dosage is scheduled to be taken.")
#end
$textToRead
  • messages.notificationTemplate.health-tip

Description:

Responsible for presenting a health tip from the category chosen for the person. Id of the health tip is available as $HEALTH_TIP_ID, more information can be retrieved through the usage of beans (see default value below). This property takes a velocity templates as the value. The output of that template will be used as a notification message.

Property format:

Velocity template

Default value:

#set($healthTipId= $HEALTH_TIP_ID.split(',').get(0))
#set($healthTipId= $Integer.parseInt($healthTipId))
#set($healthTip= $conceptDAO.getConcept($healthTipId))
#set($localeClass= $conceptDAO.getClass().forName("java.util.Locale"))
#set($healthTipText= $healthTip.getDescription().getDescription())$healthTipText
#set($dummy= $messagesService.registerResponse($personId, $personId, $message_group_id, 'SCHEDULED_SERVICE_GROUP', $healthTipId, '', 165270, '', $DateUtil.now()))
  • messages.notificationTemplate.visit-reminder

Description:

Responsible for the message used for reminding about scheduled Visits. This property takes a velocity templates as the value. The output of that template will be used as a notification message.

Property format:

Velocity template

Default value:

#set($integerClazz= $openmrsContext.loadClass('java.lang.Integer'))
#set($stringClazz= $openmrsContext.loadClass('java.lang.String'))
#set($simpleDateFormat= $openmrsContext.loadClass('java.text.SimpleDateFormat').getDeclaredConstructor($stringClazz).newInstance('yyyy-MM-dd'))
#set($visitTypeIdInteger= $integerClazz.parseInt($visitTypeId))
#set($visitPurpose= $openmrsContext.getVisitService().getVisitType($visitTypeIdInteger).getName())
#set($textToRead1= "")
#if($patient.getId().equals($actor.getId()))
	#set($textToRead1= "Hello $patient.getPersonName().toString(), You have a")
#else
	#set($textToRead1= "Hello $actor.getPersonName().toString(), Your patient has a")
#end
#set($textToRead2= "visit scheduled for $simpleDateFormat.format($simpleDateFormat.parse($dateStarted)) for the purpose of $visitPurpose.")
$textToRead1
$textToRead2

Other properties can be defined for other templates defined in the system.

Add message types

Administrators with database access are able to create a new message type at the implementation level. This will involve primarily writing an SQL query that will be used by the module in order to define how the service behaves.

How to define SQL for a service

Database level queries need to be used in order to define a new service. These can be executed by hand or implemented as a Liquibase migration if your implementation utilizes a specialized configuration module.

The process of define SQL can be represented as follows:

  1. Prepare service SQL script
  2. Prepare customizable fields for the service
  3. Service query construction
  4. Set other parameters
  5. Final limiting of prediction
  6. Print all already scheduled messages

Note:

  • All steps from the list above were described below
  • A final SQL example, which adds default messages types, can be found in the "Messages" module.
1. Prepare service SQL script

Managed services are defined within the table messages_template. Current entries can be looked up as examples. The script below is used to see only those values that are probably going to be set/changed:

SELECT service_query,
		service_query_type,
		NAME
FROM   openmrs.messages_template;

The columns to set with their descriptions:

  • uuid -r required by OpenMRS, UUID of the entry, unique, e.g. uuid()
  • creator - required by OpenMRS, id of the user that created this entry, e.g. 1 (admin)
  • service_query - value of the query. More about query construction is explained further in this document.
  • service_query_type - type of the query, right now has to be ‘SQL’ (different engines for messages might be added in the future).
  • date_created - required by OpenMRS, e.g. ‘2020-01-17 16:32:55’ or now()
  • name - the name of the service, e.g. ‘Visit reminder’

Exemplary query:

INSERT INTO openmrs.messages_template
		(uuid,
		service_query,
		service_query_type,
		date_created,
		NAME)
VALUES      (Uuid(),
		'SELECT 0;',
		'SQL',
		Now(),
		'Visit reminder');
2. Prepare customizable fields for the service

After creating the template, some fields have to be specified and assigned within the template. These need to be created as rows in the openmrs_messages_template_field table. The script below will be used to see only those values that are most likely are going to be set/changed:

SELECT NAME,
		mandatory,
		default_value,
		template_id,
		template_field_type
FROM   openmrs.messages_template_field;

The columns to set with their descriptions:

  • uuid - required by OpenMRS, UUID of the entry, unique, e.g. uuid()
  • creator - required by OpenMRS, id of the user that created this entry, e.g. 1 (admin)
  • mandatory - tells us if this field is required, true/false
  • default_value - the default value of the field, e.g. ‘Call’
  • date_created - required by OpenMRS, e.g. ‘2020-01-17 16:32:55’ or now()
  • name - the name of the field, e.g. ‘Start of messages’, that will be displayed on the Messages Edit page, also that name will by use in service query as a parameter but spaces will be replaced by ‘_’
  • template_id - the id of the service template
  • template_field_type - the type of the field, e.g. ‘START_OF_MESSAGES’. It defines the type of input you will use on the messages edit page.

The list of possible template_field_type:

  • SERVICE_TYPE - radio button: Call/SMS/Deactivate service
  • MESSAGING_FREQUENCY_DAILY_OR_WEEKLY_OR_MONTHLY - radio button Daily/Weekly/Monthly
  • MESSAGING_FREQUENCY_WEEKLY_OR_MONTHLY - radio button Weekly/Monthly
  • DAY_OF_WEEK - checkbox multichoice Monday/Tuesday/Wednesday/Thursday/Friday/Saturday/Sunday
  • DAY_OF_WEEK_SINGLE - radio button Monday/Tuesday/Wednesday/Thursday/Friday/Saturday/Sunday
  • START_OF_MESSAGES - date picker
  • END_OF_MESSAGES - radio button No end date/date picker/After X times
  • CATEGORY_OF_MESSAGE - multichoice drop down list of Health tip category

An example query to create message template fields:

INSERT INTO openmrs.messages_template_field
		(NAME,
		mandatory,
		default_value,
		template_id,
		uuid,
		creator,
		date_created,
		template_field_type)
VALUES     ('Start of messages',
		false,
		'2020-01-30',
		34,
		Uuid(),
		1,
		Now()
		,'START_OF_MESSAGES');

Service query parameters that are always available to the service query:

  • :patientId – patient id
  • :actorId - caregiver or patient id depends for who is message
  • :startDateTime/:endDateTime - required, parameters come from calendar view/scheduler or from patient_template_field_value Start of massages/End of messages depends what limit dates more. Please note that without these parameters there will be plenty of tasks scheduled for each execution which will lead to tasks’ duplication and unexpected behavior. The updates of already scheduled tasks are not supported yet.
  • :bestContactTime – best contact time set for patient or caregiver
  • All parameters from messages_template_field_value with name as described in the description of name column in messages_template_field
3. Service query construction
a. Returned value

The top level select has to return at least 4 parameters: EXECUTION_DATE, MESSAGE_ID, CHANNEL_ID, STATUS_ID with these exact names. The query can return any number of parameters without specific order.

Example:

SELECT execution_date,
		message_id,
		channel_id,
		status_id,
		visittypeid,
		locationid,
		datestarted
b. DATE_LIST
  • What is it?

It is a view with one column of the DATE type in format yyyy-mm-dd, column name is selected_date. To prepare predictions of future messages sent to patients we have to generate dates from now to an unspecified future date.

  • Operation:

First of all, we have to filter all of the dates from DATE_LIST by dateStartTime, dateEndTime to improve query performance. Example:

SELECT *
FROM   dates_list
WHERE  selected_date<=Date(:endDateTime)
		ANDselected_date>=Date(:startDateTime)

Then we have to filter by Day of week or Month. Example:

AND 
( 
	dayofmonth(selected_date)<IF(:Frequency_of_the_message='Weekly'
	OR
	:Frequency_of_the_message='Daily',32,8)
)
AND 
:Week_day_of_delivering_message LIKE
concat('%',dayname(selected_date),'%')

After that, it is time to merge raw DATE to the best contact time. It should be in the first subselect. Example:

SELECT Timestamp(selected_date,:bestContactTime) AS EXECUTION_DATE
4. Set other parameters

Now it’s time to get other needed parameters by joining tables together to make operations you need and add parameters in the first subselect. Example:

SELECT Timestamp(selected_date,:bestContactTime) AS execution_date,
		message_id,:
		Service_type AS channel_id,
		visittypeid,
		locationid,
		visit_dates AS datestarted(...)
JOIN 
		(
				SELECT date_started AS visit_dates,
						visit_type_id AS visittypeid,
						location_id AS locationid
				FROM visit
				WHERE patient_id = :patientId)dates_of_visit

Note: The alias you set in service query will be the key for parameter map sent to the call flow module.

5. Final limiting of prediction

Right now you need to make sure that all returns you have are in range of startDateTime and endDateTime (time in that case) and also check that the service is not deactivated. Example:

WHERE execution_date <= :endDateTime
AND 
execution_date > = :startDateTime
AND 
execution_date >
get_prediction_start_date_for_visit(:patientId,:actorId)
AND 
channel_id != 'Deactivate service'

Where ‘get_prediction_start_date_for_visit’ can looks like:

DELIMITER $$
CREATE FUNCTION `get_prediction_start_date_for_visit`(patient_idlong,actor_id long)
returns datetime
BEGINDECLARE mssg_date DATETIME;SET mssg_date = 
	(
			SELECT execution_date
			FROM (
		SELECT mssg.msg_send_time AS execution_date,
		1                  AS message_id,
		mss.channel_type AS channel_id,
		mss.status AS status_id
FROM messages_scheduled_servicemss
JOIN messages_patient_templatempt
ON mpt.messages_patient_template_id = mss.patient_template_id
JOIN messages_templatemt
ON mt.messages_template_id = mpt.template_id
JOIN messages_scheduled_service_groupmssg
ON mssg.messages_scheduled_service_group_id = mss.group_id
WHERE mt.NAME = 'Visit reminder'
	AND mpt.patient_id = patient_id
	AND mpt.actor_id = actor_id
	ORDER BY 1 DESC) a limit 1);
RETURNIF(mssg_date > Now(), mssg_date, Now());
END
$$
6. Print all already scheduled messages

Last thing to implement is to get all already scheduled messages to print on the calendar view. Example:

UNION 
SELECT   mssg.msg_send_time AS execution_date,
		1                  AS message_id,
		mss.channel_type AS channel_id,
		mss.status AS status_id,
		NULL AS visittypeid,
		NULL AS locationid,
		NULL AS datestarted
FROM     messages_scheduled_servicemss
JOIN     messages_patient_templatempt
ON       mpt.messages_patient_template_id = mss.patient_template_id
JOIN     messages_templatemt
ON       mt.messages_template_id = mpt.template_id
JOIN     messages_scheduled_service_groupmssg
ON       mssg.messages_scheduled_service_group_id = mss.group_id
WHERE    mt.NAME = 'Visit reminder'
AND      mpt.patient_id = :patientId
AND      mpt.actor_id = :actorId
AND      mssg.msg_send_time>= :startDateTime
AND      mssg.msg_send_time<= :endDateTime
ORDER BY 1 DESC;

Steps after SQL implementation

With defined SQL put in the database, to create a fully working service, following actions need to be fulfilled:

  • Create call flow in the call flows designer
  • Create global properties defining the SMS content of message (see examples of existing services)
  • Update the “messages.notificationTemplate.injectedServices” global property

Consent validation

Consent validation is activated by default, but by the suitable global property it can be turned off.

To access global property, click “Manage Global Properties” button on the admin panel.

Next, find “message.consent.validation” on the list and click pen icon in the property’s row.

On the edit global property page change value from “true” to “false” and save it. This action will remove consent validation for both patient and caregiver.

Ad Hoc Messages

This page allows you to send ad hoc messages and define the content of built-in SMS and Calls that will be sent to the target patient population.

To access the “Ad hoc Message” page, go to the "Advanced Administration" page and clickon the "Ad hoc Message" link in the "CFL Module" section.

You will be redirected to the “Ad hoc Message” page.

Parameters

In the parameters section you have to define when and what message you want to send.

First you have to define when you want to send the message. Choose a date from the date picker and time by using the time slicers.

For channel you can pick sms and/or call.

For call, you need to pass the Call Flow name of the message voicefile. This file will then be used for the call.

For SMS, you need to input the text using a velocity template.

Target patients

Generally, there will be 3 types of filters from backend perspective (from user's perspective there will be no difference):

  1. Filters on patient fields, e.g.: gender, date of birth
    • Configurable via global parameter
  2. Filters on any person attribute, e.g.: Regimen ('Vaccination program'), LocationAttribute
    • Configurable via global parameter
  3. Special, calculated filters, e.g.: Number of received dosages (related to Regimen, calculated based on occurred visits of visit type configured by special global parameter, the parameter defines vaccination process - how many dosages, what visits, during which visit a vaccination is done)

In case a selection is chosen, the following options will be there (all optional, can be combined):

  • Country
  • Site location
  • Regimen
  • Number of received dosages
  • Gender
  • Minimum age
  • Maximum age

Once you've selected the filters, you can see which patients (if any) are in scope and are bound to receive the message.

If no parameters are selects, all patients will be in scope.

After reviewing the patient list, you can click "Send" to send the message. You will receive a final confirmation pop-up before the message is send.

After pressing OK, you get the confirmation that the message has been send to the patients.

Responsive messages

Two-way communication with WhatsApp

Connect For Life™ allows two-way communication with WhatsApp that will respond to messages send out to a predefined number which will connect to the application. For WhatsApp you need a WhatsApp business account or a sandbox environment to receive the messages. For more information on how to set this up, please refer to the technical manual regarding messaging.

Message from number not recognized

If a number, that doesn't exist in Connect For Life™, sends a message to the predefined number, you will receive the standard response "I'm sorry, we don't recognize your phone number.". This default message can be customized.

Message from number that is recognized

If a number, that is present in Connect For Life™, sends a message, the message sent will be valided to the defined respons script to see which answer the system will send back. There are two options:

  1. Answer that is not part of the defined respons script
  2. Answer that is recognised in the defined respons script

If an answer is not recognised in the defined respons script, a standard default answer will be send together with the menu so the user knows which starting responses get accepted by the respons script.

If the answer is recognised, the linked respons will be send back to the user.

Clone this wiki locally