Document toolboxDocument toolbox

Scheduling API

The scheduling system contains the constructs to schedule a participant to perform “active” assessments (assessments that the system is going to prompt the user to do at specific times… “passive” assessments might be associated with these active tasks, but this isn’t currently part of the Bridge server’s domain model). Protocols, study arms, and assessments are covered in a separate design document. This document discusses the association of assessments with scheduling information. Schedules consist of one or more sessions, describing when a set of assessments are to be done by a study participant.

Schedules are associated one-to-one with study arms in a study, but the scheduling system will stand alone from studies (since it is being developed first). We may temporarily include some information in Schedule that will eventually belong in or be overridden by information in a Protocol.

Design-Time APIs

Study designers create a schedule which contains one or more sessions. Each session specifies scheduling information about a list of assessments, some information about how the assessments should be performed, and notification options.

Study participants retrieve these schedules expanded into a timeline of activities the participant will be asked to perform across the lifetime of the study. The client can save adherence records as the tasks are performed, which are used to determine adherence to the study protocol, but that can also include additional information the client wants to store for UI state, graphing, or other purposes.

Schedules

Schedules contain a list of sessions. A study arm has one and only one schedule, although the same schedule may be used in multiple study arms. The only way in which Bridge server alters its behavior based on the participant is through different schedules, so almost by definition, allocation to a study arm varies the schedule, if it varies anything.

Schedules are globally available to the members of the organization that owns them. They can be referenced from any Study Arm in any Protocol the organization owns. (Protocols and schedules cannot be transferred to other organizations.)

Method

Path

Description

Method

Path

Description

GET

/v5/schedules

Returns schedules owned by the caller's organization. May not include detail information like the sessions in each schedule.

POST

/v5/schedules

Create new schedule. Will automatically be owned by the caller’s organization.

GET

/v5/schedules/{guid}

Get a schedule by its GUID. Will include the fully expanded list of sessions and their assessments.

POST

/v5/schedules/{guid}

Update a schedule. Send a body payload and/or query parameters. If a body payload is sent, the schedule is updated. After this optional step, if "guid" query parameters are included, they are used to reorder the sessions in the schedule [open to better semantics in query parameters, but a single parameter seems janky]. If a guid parameter is given, there must be a parameter for every session in the schedule. You CANNOT use this API to add or remove sessions because sessions do not exist outside of schedules (to delete sessions, call the delete session endpoint; to add a session, call the add session endpoint, and then reorder the sessions if needed).

DELETE

/v5/schedules/{guid}

Delete a schedule (probably an admin-only capability until we can verify the schedule is not in use).

Here is an example of the JSON for a schedule. When submitting to the server, the sessions array would be ignore; sessions could be edited through the sessions API and re-ordered with the use of query string parameters.

{ "title":"Primary Study Schedule", "guid":"321af73f", "sessions":[ { "name":"Take the Tapping Test", "guid":"8bda9fa9", "startEventId":"StartOfStudy", "interval":7, "bundled":false, "randomized":false, "notifyAt":"AT_START_OF_WINDOW", "reminderAt":"BEFORE_WINDOW_END", "allowSnooze":true, "messagesByLocale":{ "en":{ "subject":"Subject", "body":"Body", "langCode":"en", "type": "Message" } }, "assessments":[ { "guid":"b58733b4", "configGuid": "c69844c5", "title":"Pre-Test Questions", "minutesToComplete":2, "type":"Assessment" }, { "guid":"e22ab243", "configGuid":"f33bc354", "title":"Test", "minutesToComplete":5, "type":"Assessment" } ], "sessionWindows":[ { "guid":"dc28abcf", "startTime":"08:00", "expiresAfter":"PT2H", "persistent":false }, { "guid":"2cz8deff", "startTime":"14:00", "expiresAfter":"PT2H", "persistent":false }, { "guid":"682cgh6f", "startTime":"18:00", "expiresAfter":"PT2H", "persistent":false } ] } ] }

Sessions

“Ecological momentary assessment (EMA) involves repeated sampling of subjects' current behaviors and experiences in real time, in subjects' natural environments.” ("Ecological momentary assessment," Shiffman et al, Annual Review of Clinical Psychology 4: 1-32, 2008).

Study Bursts in mPower are an example of an EMA assessment paradigm. In our domain model, EMA-style assessments can be implemented through a Session. A session groups a list of assessments and specifies one or more time windows when they should be performed on a particular day of the study. Of note:

  1. Assessments have to be associated to schedules through a session, so even sessions of one assessment are a reasonable design construct;

  2. The length of a session is a day, unless the duration of one of the session’s time windows extends the session length to multiple days.

  3. Permanent sessions can be completed as many times as the participant wants to do them.

  4. Period values in this and other constructs cannot mix time units, so for example, the expiration of a time window cannot be “P3W2D,” it must be “P23D.”

Method

Path

Description

Method

Path

Description

POST

/v5/schedules/{scheduleGuid}/sessions

Create a session in the schedule.

GET

/v5/schedules/{scheduleGuid}/sessions/{sessionGuid}

Get an individual session along with all of its assessments (this might not be needed if all the information is returned in a schedule).

POST

/v5/schedules/{scheduleGuid}/sessions/{sessionGuid}

Send a body payload and/or query parameters. If a body payload is sent, the session is updated. After this optional step, if "guid" query parameters are included, they are used to reorder the assessments in the session. Because assessments are global and not dependent on sessions, you CAN (and should) use this api to add or remove assessments from the session.

DELETE

/v5/schedules/{scheduleGuid}/sessions/{sessionGuid}

Delete a session, removing it from the schedule.

Assessments

The assessment APIs have been implemented. One outstanding question is whether or not sessions can include shared assessments. The design was originally not going to allow this, but it will lead to a large amount of copying from the shared library to MTB. If we allow linking to shared assessments, developers would only have to import shared assessments when they wished to revision them or use them as the basis for new assessment development.

Runtime APIs

The client retrieves three pieces of information in order to determine the schedule for a participant:

  1. A map of event times keyed by event identifiers. Each Session in a schedule begins if (and only if) that specific event ID appears in the user’s event map with a timestamp;

  2. A Timeline that takes the design-time schedule, and calculates every performance of every assessment and session in the schedule (giving the client GUIDs for each performance, so that data can be related back to the participant’s study schedule;

  3. An adherence API to retrieve the state of sessions and assessments that have already been performed.

The client should proceed through the following algorithm with this information:

  1. Calculate the daysSinceEventId value for every event in the activity event map, using the events timestamp (0-indexed and not an absolute time difference, but a measure to the day);

  2. For each scheduled session instance in the timeline, if the startEventId’s daysSinceEventId value falls outside of the startDay to endDay range of the session instance, or the local time falls outside of the time window given by the scheduled session, then the session is not active;

  3. The list of potentially active scheduled session GUIDS are sent to the adherence API, which returns state records for these sessions…if the associated record has a finishedOn timestamp, it is not active;

  4. All remaining scheduled activities should be shown to the user in the UI and can be performed by the participant.

The timeline and finished adherence records can be cached pretty aggressively for better performance.

Calculating progress through a study: A user’s timeline can be compared to their adherence records to determine their overall progress through the study in abstract. This can be plotted to give a visual representative to study coordinators, for example.

Calculating adherence: To calculate adherence, scheduled sessions must be removed from the timeline that the user cannot yet perform, which requires calculating, for every event, the user’s current day count from that event. (Then scheduled sessions beyond those days are then removed.) The server knows the caller’s initial time zone and activity event timestamps, so it can calculate these values, though they may be a bit off from the client’s view of adherence.

Caching. Timelines can be cached for a long period of time. Bridge will implement caching and 304 responses through support of the If-Modified-Since and Last-Modified headers (reference). Any change to a schedule or its sessions will update the schedule’s modifiedOn timestamp, and this timestamp will be used to return a 304 for cases where the client’s If-Modified-Since header is more recent that the schedule’s modifiedOn timestamp. Clients opt out of 304 responses by not sending the If-Modified-Since header.

Activity Events

These APIs are currently being implemented, and include:

Method

Path

Description

Method

Path

Description

GET

/v5/studies/{studyId}/participants/self/activityEvents

Get a map of activity event IDs and their timestamps. If the event has not occurred, it does not exist in the map, and no sessions triggered by that event should appear in the participant’s list of tasks to perform.

POST

/v5/studies/{studyId}/participants/self/activityEvents

Create a new activity event (if the event ID already exists, it will be updated if the event type is mutable, or else this call is ignored).

DELETE

/v5/studies/{studyId}/participants/self/activityEvents

Delete an activity event.

GET

/v5/studies/{studyId}/participants/{userId}/activityEvents

(Same API, but for study coordinator)

POST

/v5/studies/{studyId}/participants/{userId}/activityEvents

(Same API, but for study coordinator; we already know this is needed in MTB)

DELETE

/v5/studies/{studyId}/participants/{userId}/activityEvents

(Same API, but for study coordinator)

Timelines

After a participant has been enrolled in a study and their consent has been completed, they can retrieve a timeline of assessments to perform (without reference to their study arm assignment). If preconditions are not met, the scheduling API will return a 412 with information about the specific requirements that have yet to be met.

A timeline produces a record for every instance of every session window in every session, as many times as that session window will repeat over the course of a study . Each such instance will have a unique GUID that the client must include with uploads of research data. Some of these records can be marked as persistent which indicates that they can be uploaded more than once to the upload and adherence record systems.

Day counts are 0-indexed, and ranges (startDay to endDay) are inclusive. If a session is seven days long and starts at the beginning of a study, the assessment is available from Day 0 to Day 6 (until it is completed or day 6 has passed).

Currently, startTime and duration are used to suggest a time of day when the assessment can be started. We expect to augment this with use-relative abstract time identifiers (e.g. “breakfast”) that the user can configure for their personal schedule.

Some further edge cases:

  • if the startTime and duration go beyond the current day, it will push out the endDay until the session range includes the duration of the assessment;

  • if the days of a session extend past the total number of days of a protocol, the entire session is not included.

Method

Path

Description

Method

Path

Description

GET

/v5/studies/{studyId}/participants/self/timeline

Enrolled and allocated caller retrieves the schedule as a timeline. Initially, there will only be one schedule per study, so no selection really occurs, but eventually, the timeline returned will depend on the caller’s study arm allocation. This endpoint will implement HTTP caching semantics through the If-Modified-Since header.

GET

/v5/studies/{studyId}/participants/{userId}/timeline

The timeline for a specific user. This call does not require the administrative user to know which schedule has been assigned to the study participant. It will later be paired with adherence information.

GET

/v5/schedules/{guid}/timeline

The timeline as an abstraction for study designers, that can be called at design time and requires no participant accounts.

Timeline payload example:

{ "duration": "P1W", "assessments": [ { "key": "dc1233b8dc1233b8", "guid": "AJp1u9KvN0Fwbx6uf-xRyPIP", "appId": "shared", "identifier": "psm", "revision": 2, "label": "Arranging Pictures", "minutesToComplete": 5, "colorScheme": { "background": "#FFFFFF", "foreground": "#CCE5D5", "activated": "#CCE5D5", "inactivated": "#D8E4DC", "type": "ColorScheme" }, "type": "AssessmentInfo" }, { "key": "79e1050f79e1050f", "guid": "M1enXAt6hZE-eUyH2XIUnAu6", "appId": "shared", "identifier": "vocabulary", "revision": 2, "label": "Word Meaning", "minutesToComplete": 2, "colorScheme": { "background": "#FFFFFF", "foreground": "#95CFF4", "activated": "#95CFF4", "inactivated": "#BCD9EC", "type": "ColorScheme" }, "type": "AssessmentInfo" } ], "sessions": [ { "guid": "Dxa9zc0jRyfOF8rCtYBd1p2O", "label": "Session1", "startEventId": "study_start_date", "performanceOrder": "participant_choice", "minutesToComplete": 7, "type": "SessionInfo" } ], "schedule": [ { "instanceGuid": "O6VhPYre4fm2o_72P0yPjA", "startDay": 0, "endDay": 7, "startTime": "08:00", "assessments": [ { "refKey": "79e1050f79e1050f", "instanceGuid": "BKe26hUXwYnwkCiTDK4_Pw", "type": "ScheduledAssessment" }, { "refKey": "dc1233b8dc1233b8", "instanceGuid": "1wd_1QlUHAy_Xxj1PtH5Hg", "type": "ScheduledAssessment" } ], "refGuid": "Dxa9zc0jRyfOF8rCtYBd1p2O", "type": "ScheduledSession" } ], "type": "Timeline" }

Adherence Records

All state information (whether an assessment was started and/or finished, any UI state associated with a given execution of an assessment, anything like scoring of an assessment) is saved separately from the timeline on a per-user basis. These are known as adherence records. Of note:

  1. The set of adherence records is sparse, since scheduled sessions that the user hasn’t interacted with won’t have adherence records;

  2. Both session and assessment instance records can be saved and retrieved. Bridge is agnostic as to whether the client operate in terms of session or assessment state (or both);

  3. The client can invent GUIDs to store arbitrary records alongside records that are associated to a schedule (with a noteworthy loss of search capabilities that are tied to schedule metadata, such as time ranges or records of a given assessment type);

  4. A GUID may return more than one adherence record (e.g. persistent time windows allow participants to repeatedly perform an assessment). These records must differ in their startedOn timestamps;

  5. The client can retrieve records in batch by supplying any of a number of search parameters, described below.

Method

Path

Description

Method

Path

Description

POST

/v5/studies/{studyId}/participants/self/adherence

API for study participant to create a state or update one or more state information records.

POST

/v5/studies/{studyId}/participants/self/adherence/search

Search for records using a search parameters object, described below.

GET

/v5/studies/{studyId}/participants/{userId}/adherence/search

API for study coordinators or other administrators to retrieve state information records. based on search criteria, defined below.

Here are the arguments for searching (all criteria are additive so it’s certainly possible to create a set of search criteria that will produce no results):

AdherenceRecordSearch { // Include assessment records, session records, or if null...both? Note that // where necessary, a search can be done to find a set of session instances, // in order to return their assessment instances, or the reverse, a search // on assessment instances can then return their parent session instances. recordTypes: AdherenceRecordType{assessment, session} [default = both] // Session or assessment instance GUIDs; any records that exist under these // GUIDs for the caller will be returned. If the assessment is a persistent // assessment, all adherence records for that assessment will be returned // unless includeRepeats = false. instanceGuids: String[] // Return adherence records for these assessmentIds (as types). assessmentIds: String[] // Return adherence records for this session (as a type). sessionGuids: String[] // Include multiple runs of assessments in persistent time windows? These // will have the same GUIDs but must have different startedOn timestamps. // If includeRepeats = false, only the record with the earliest or latest // startedOn value will be returned, depending on sortOrder. includeRepeats: Boolean [default = true] // Only retrieve records whose event timestamps are identical to the values // that are supplied in the map. This is necessary for sessions that are // based on mutable events, or the client will not be able to determine when // the session time series should be performed again. (We may also provide a // means to use the server’s representation of these events, though there // could be discrepancies). eventTimestamps: Map<String, DateTime> [default = all records, any timestamp] // If provided, startedOn values of records must be on or after the startTime // and on or before the endTime. startTime: DateTime endTime: DateTime // this API is paged offsetBy: Integer [default = 0] pageSize: Integer [default = 500] // Sort by the startedOn value of the records, earliest first (ASC) or // most recently started first (DESC) sortOrder: SortOrder{asc, desc} }

Absent any search criteria, all adherence records will be returned (paged), ordered by the startedOn values (because these records are sparse, any records that exist must represent a session or assessment that has been started, so the startedOn field is required).

The primary key of the adherence record is the guid + startedOn timestamp, so multiple copies must have different startedOn timestamps (they are also differentiated by study and user; the same schedule can be executed by a user in two different studies, for example, and the state records are separate). Queries are resolved by joining the adherence and timeline metadata tables.

Measuring adherence to mutable event sessions

It is possible to query the adherence records to match a timeline against its performance. However, mutable events allow for a session time series to be performed more than once, which creates a challenge for measuring adherence. This is because the event timestamps table does not contain historical information—when an event is changed, the old timestamp is lost, though it may be represented by the event timestamps that are recorded in the adherence table.

For this reason, the AdherenceService will provide a means to retrieve the timestamps for each event, via the adherence table. For mutable events, every timestamp indicates a separate attempt to run through that portion fo the schedule. However, if the user does nothing after an event update, the timestamp won’t be in the adherence table. For this reason, when a mutable event is about to be updated, the adherence table will be queried, and if the event timestamp is not present, it will be added via a dummy record for the first session of the schedule that has a startedOn timestamp of zero.

Adherence Service

Uploads

We currently add information to Bridge Uploads in the HealthDataService #makeRecordFromSubmission() method. The uploads from the client will need to contain a scheduled session or scheduled assessment GUID so we can lookup and transfer over a rich set of metadata to the health data records (either in the JsonNode or Map in Bridge’s health data records). This data depends on the upload being related to a schedule, and includes:

Metadata

Description

Metadata

Description

assessmentInstanceGuid

 

assessmentGuid

 

assessmentId

 

assessmentRevision

 

sessionInstanceGuid

 

sessionGuid

 

sessionStartEventId

 

sessionInstanceStartDay

 

sessionInstanceEndDay

 

timeWindowGuid

 

scheduleGuid

 

scheduleModifiedOn

 

schedulePublished

 

appId

 

Domain Model (Java)

 



Persistence Model (SQL)

TBD